// Copyright (c) 2004-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:
// SPUD event manager
// WINSCW compiler has problems disambiguating TMetaDes8 and Meta::TMetaDes8 so
// I prevent metabuffer.h from being recursively included from other headers
// by defining its include guard.
// 
//

/**
 @file
 @internalComponent
*/
#define METABUFFER_H

#include "spudman.h"
#include "bindman.h"
#include "mux.h"

#include <comms-infras/dbaccess.h>
#include "rpdpfsminterface.h"
using namespace SpudMan; // Access the SpudFsm event names

#include <etelpckt.h>
#include <comms-infras/commsdebugutility.h>
#include <in_sock.h>
#include <networking/qos_if.h>
#include <nifman.h>
#include <comms-infras/es_config.h>

#include <networking/umtsgprs_subconnprovfactory.h>
#include <ss_glob.h>
#include <ss_subconnprov.h>

const TUint KQosPlugInProtocolId = 360; //< Plug-in protocol ID

static const TInt KMaxInetAddrPrintSize = 50; //This should be big enough for an IPv6 printout


#ifdef __FLOG_ACTIVE

_LIT(KSpudUnknownLit, "Unknown event");

_LIT(KSpudSoIfControllerPlugInLit, "KSoIfControllerPlugIn");
_LIT(KSpudLitKRegisterEventHandler, "KRegisterEventHandler");
_LIT(KSpudContextSetEventsLit, "KContextSetEvents");
_LIT(KSpudNifSetDefaultQoSLit, "KNifSetDefaultQoS");
_LIT(KSpudContextCreateLit, "KContextCreate");
_LIT(KSpudContextDeleteLit, "KContextDelete");
_LIT(KSpudContextActivateLit, "KContextActivate");
_LIT(KSpudContextQoSSetLit, "KContextQoSSet");
_LIT(KSpudContextTFTModifyLit, "KContextTFTModify");
_LIT(KSpudGetNegQoSLit, "KGetNegQoS");
_LIT(KSpudContextModifyActiveLit, "KContextModifyActive");
_LIT(KInitialisePdpFsmLit, "KInitialisePdpFsm");


static const TDesC *SpudGuQoSEventToText(TInt aEvent)
	{
	switch(aEvent)
		{
	case KSoIfControllerPlugIn:
		return &KSpudSoIfControllerPlugInLit;
	case KRegisterEventHandler:
		return &KSpudLitKRegisterEventHandler;
	case KContextSetEvents:
		return &KSpudContextSetEventsLit;
	case KNifSetDefaultQoS:
		return &KSpudNifSetDefaultQoSLit;
	case KContextCreate:
		return &KSpudContextCreateLit;
	case KContextDelete:
		return &KSpudContextDeleteLit;
	case KContextActivate:
		return &KSpudContextActivateLit;
	case KContextQoSSet:
		return &KSpudContextQoSSetLit;
	case KContextTFTModify:
		return &KSpudContextTFTModifyLit;
	case KGetNegQoS:
		return &KSpudGetNegQoSLit;
	case KContextModifyActive:
		return &KSpudContextModifyActiveLit;
	case KInitialisePdpFsm:
		return &KInitialisePdpFsmLit;
	default:
    	return &KSpudUnknownLit;
		}
	}
	
_LIT(KSpudContextDeleteEventLit, "KContextDeleteEvent");
_LIT(KSpudContextActivateEventLit, "KContextActivateEvent");
_LIT(KSpudContextParametersChangeEventLit, "KContextParametersChangeEvent");
_LIT(KSpudContextBlockedEventLit, "KContextBlockedEvent");
_LIT(KSpudContextUnblockedEventLit, "KContextUnblockedEvent");
_LIT(KSpudNetworkStatusEventLit, "KNetworkStatusEvent");
_LIT(KSpudContextQoSSetEventLit, "KContextQoSSetEvent");
_LIT(KSpudContextTFTModifiedEventLit, "KContextTFTModifiedEvent");
_LIT(KSpudPrimaryContextCreatedLit, "KPrimaryContextCreated");
_LIT(KSpudSecondaryContextCreatedLit, "KSecondaryContextCreated");
_LIT(KSpudGetNegQoSEventLit,"KGetNegQoSEvent");
_LIT(KSpudContextModifyActiveEventLit, "KContextModifyActiveEvent");
_LIT(KPdpFsmShuttingDownLit, "KPdpFsmShuttingDown");

static const TDesC *SpudFsmEventToText(TInt aEvent)
	{
	switch(aEvent)
    	{
	case KContextDeleteEvent:
		return &KSpudContextDeleteEventLit;
	case KContextActivateEvent:
		return &KSpudContextActivateEventLit;
	case KContextParametersChangeEvent:
		return &KSpudContextParametersChangeEventLit;
	case KContextBlockedEvent:
		return &KSpudContextBlockedEventLit;
	case KContextUnblockedEvent:
		return &KSpudContextUnblockedEventLit;
	case KNetworkStatusEvent:
		return &KSpudNetworkStatusEventLit;
	case KContextQoSSetEvent:
		return &KSpudContextQoSSetEventLit;
	case KContextTFTModifiedEvent:
		return &KSpudContextTFTModifiedEventLit;
	case KPrimaryContextCreated:
		return &KSpudPrimaryContextCreatedLit;
	case KSecondaryContextCreated:
		return &KSpudSecondaryContextCreatedLit;
	case KGetNegQoSEvent:
    		return &KSpudGetNegQoSEventLit;
    case KContextModifyActiveEvent:
    	return &KSpudContextModifyActiveEventLit;
   	case KPdpFsmShuttingDown:
    	return &KPdpFsmShuttingDownLit;
    default:
    	return &KSpudUnknownLit;
    	}
	}

_LIT(KSpudStateSpudInactiveLit, "ESpudInactive");
_LIT(KSpudStateSpudHaveQosLit, "ESpudHaveQos");
_LIT(KSpudStateSpudCreatingPrimary, "ESpudCreatingPrimary");
_LIT(KSpudStateSpudStartingPrimaryLit, "ESpudStartingPrimary");
_LIT(KSpudStateSpudStartingPrimaryLowerNifLit, "ESpudStartingPrimaryLowerNif");
_LIT(KSpudStateSpudStartingSecondaryLit, "ESpudStartingSecondary");
_LIT(KSpudStateSpudStartingSecondaryLowerNifLit, "ESpudStartingSecondaryLowerNif");
_LIT(KSpudStateSpudGettingNegQoSLit, "ESpudGettingNegQoS");
_LIT(KSpudStateSpudUpLit, "ESpudUp");
_LIT(KSpudStateSpudFlowOffLit, "ESpudFlowOff");
_LIT(KSpudStateSpudSuspendedLit, "ESpudSuspended");
_LIT(KSpudStateSpudFlowOffAndSuspendedLit, "ESpudFlowOffAndSuspended");
_LIT(KSpudStateSpudLinkDownLit, "ESpudLinkDown");
_LIT(KSpudStateSpudContextDeleteLit, "ESpudContextDelete");
_LIT(KSpudStateSpudWaitLinkDownLit, "ESpudWaitLinkDown");
_LIT(KSpudStateSpudWaitBinderDeleteLit, "ESpudWaitBinderDelete");
_LIT(KSpudStateUnknownLit, "Unknown spud state");
	
static const TDesC *SpudStateToText(TSpudContextStates aState)
	{
	switch(aState)
    	{
	case ESpudInactive:
		return &KSpudStateSpudInactiveLit;
	case ESpudHaveQos:
		return &KSpudStateSpudHaveQosLit;
	case ESpudCreatingPrimary:
		return &KSpudStateSpudCreatingPrimary;
	case ESpudStartingPrimary:
		return &KSpudStateSpudStartingPrimaryLit;
	case ESpudStartingPrimaryLowerNif:
		return &KSpudStateSpudStartingPrimaryLowerNifLit;
	case ESpudStartingSecondary:
		return &KSpudStateSpudStartingSecondaryLit;
	case ESpudStartingSecondaryLowerNif:
		return &KSpudStateSpudStartingSecondaryLowerNifLit;
	case ESpudGettingNegQoS:
		return &KSpudStateSpudGettingNegQoSLit;
	case ESpudUp:
		return &KSpudStateSpudUpLit;
	case ESpudFlowOff:
		return &KSpudStateSpudFlowOffLit;
	case ESpudSuspended:
		return &KSpudStateSpudSuspendedLit;
	case ESpudFlowOffAndSuspended:
		return &KSpudStateSpudFlowOffAndSuspendedLit;
	case ESpudLinkDown:
		return &KSpudStateSpudLinkDownLit;
	case ESpudContextDelete:
		return &KSpudStateSpudContextDeleteLit;
	case ESpudWaitLinkDown:
		return &KSpudStateSpudWaitLinkDownLit;
	case ESpudWaitBinderDelete:
		return &KSpudStateSpudWaitBinderDeleteLit;
	default:
    	return &KSpudStateUnknownLit;
    	}
	}

#endif

CSpudMan::CSpudMan(CNifIfFactory& aFactory, MNifIfNotify* aNotify)
	: CNifIfLink(aFactory),
	iContextStatusOverride(RPacketContext::EStatusUnknown)
	{
    iNotify = aNotify;
    ASSERT(iNotify);
	__FLOG_OPEN(KSpudFirstTag,KSpudLog);
    __FLOG_0(_L("CSpudMan::CSpudMan"));
	}

CSpudMan::~CSpudMan()
   {
   if (iBindMan)
      {
      // Only log if ConstructL() was called, where logger was initialized
      __FLOG_0(_L("CSpudMan::~CSpudMan"));
      }
   
   
   if (AreQoSEventsEnabled())
      {
      // Spud is being destroyed by Nifman. Tell GUQoS to stop bothering SPUD.
      // GUQoS returns the favour by turning off the NIF events within this very call.
      // *********************************************************************************************
      // N.B.: "DEF055691 	GUQoS crashes at shutdown": This defect results in GUQoS crash if the stack
      // closes flows after the NIF proxy was deleted by GUQoS, as a result of KNetworkInterfaceDown event.
      // Because of this defect, we cannot signal KNetworkInterfaceDown from anywhere but here. Once it is fixed,
      // calls from the appropriate places will be enabled, and the line below will not be strictly necessary.
      SendNetworkStatusEvent(KNetworkInterfaceDown, RPacketService::EStatusUnattached);
      // For safety, we should call this from here in any case.
      //**********************************************************************************************
      }
   
   delete iParkedDefaultQoS;
   delete iBinderSweeperNotifierCb;
   
   delete iBindMan;
   iPdpFsmInterface.Close();
   iSipServerAddr.Close();
   __FLOG_CLOSE;
   }

/**
Panics the current thread.

@param aReason Panic reason code
*/
void CSpudMan::Panic(TInt aReason) const
	{
    __FLOG_1(_L("CSpudMan::Panic with reason %d"), aReason);
	User::Panic(KSpudName, aReason);
	}

/**
Construct the Link Protocol Object

@param aBindMan Pointer to BindMan object (ownership is transferred)
@leave leaves if could not allocate memory
*/
void CSpudMan::ConstructL(CBindMan* aBindMan)
	{
    __FLOG_1(_L("CSpudMan starting %x"), iNotify);


	iBindMan = aBindMan;
	ASSERT(iBindMan);
	
	// The lower NIF binder deletion & Nifman notification callback
	iBinderSweeperNotifierCb = new (ELeave) CBinderSweeperNotifierCb(*this);
	}


/**
Implements the Control method of CNifBase - used for retrieval of the P-CSCF (Sip Server) address
*/	
TInt CSpudMan::Control(TUint aLevel,TUint aName,TDes8& aOption, TAny*)
	{
	if (aLevel == KCOLConfiguration && aName == KConnGetSipServerAddr)
		{
		if (aOption.Length() != sizeof (SSipServerAddr))
			{
			__FLOG(_L("CSpudMan::Control - Invalid Descriptior - Descriptor Must contain an SSipServerAddr"));
			Panic();
			}
		SSipServerAddr* sipServerAddr = reinterpret_cast<SSipServerAddr*>(const_cast<TUint8*>(aOption.Ptr()));
		if (sipServerAddr->index < 0 || sipServerAddr->index >= iSipServerAddr.Count())
			{
			__FLOG_1(_L("CSpudMan::Control - Index out of range : value = %d"), sipServerAddr->index);
			return KErrNotFound;
			}
		sipServerAddr->address = iSipServerAddr[sipServerAddr->index];
#ifdef __FLOG_ACTIVE
		TBuf<KMaxInetAddrPrintSize> buf;
		sipServerAddr->address.Output(buf);
		__FLOG_2(_L("CSpudMan::Control Address %S returned for index %d"), &buf, sipServerAddr->index);
#endif
		return KErrNone;				
		}
	return KErrNotSupported;	
	}


/**
Retrieve any GPRS settings required from CommDB

@param aConfigGprs Context configuration parameters to be filled in
*/
void CSpudMan::RetrieveGprsConfig(RPacketContext::TContextConfigGPRS& aConfigGprs) const
	{
	TUint32 pdpType(0);
	Notify()->ReadInt(TPtrC(GPRS_PDP_TYPE), pdpType);
 	__FLOG_1(_L8("ReadInt GPRS_PDP_TYPE [%d]"), pdpType);
	aConfigGprs.iPdpType = STATIC_CAST(RPacketContext::TProtocolType, pdpType);

	Notify()->ReadDes(TPtrC(GPRS_APN), aConfigGprs.iAccessPointName);
  	__FLOG_1(_L8("ReadDes GPRS_APN [%S]"), &aConfigGprs.iAccessPointName);

		
	TBool fromServer;
	aConfigGprs.iPdpAddress.SetLength(0);
	Notify()->ReadBool(TPtrC(GPRS_IP_ADDR_FROM_SERVER), fromServer);
	if (!fromServer)
	    {
    	Notify()->ReadDes(TPtrC(GPRS_IP_ADDR), aConfigGprs.iPdpAddress);
	    }
	

    // We can only use IPv4 or IPv6 - we use the first one listed in the IfNetworks field
    TBuf<KCommsDbSvrMaxFieldLength> networks;
	Notify()->ReadDes(TPtrC(LAN_IF_NETWORKS), networks);

	ASSERT(networks.Length() > 0); // If the IfNetworks field is empty there is a serious misconfiguration
	
	TInt pos = networks.Find(_L(","));
	if (pos == KErrNotFound)
	    {
	    pos = networks.Length();
	    }
	TPtrC protocol(networks.Ptr(), pos);
	_LIT(KIp4, "ip");
	_LIT(KIp6, "ip6");
	if (protocol.CompareF(KIp4) == 0)
	    {
	    // IPv4 settings
    	Notify()->ReadBool(TPtrC(GPRS_IP_DNS_ADDR_FROM_SERVER), fromServer);
    	if (!fromServer)
    	    {
    	    Notify()->ReadDes(TPtrC(GPRS_IP_NAME_SERVER1), aConfigGprs.iProtocolConfigOption.iDnsAddresses.iPrimaryDns);
    	    Notify()->ReadDes(TPtrC(GPRS_IP_NAME_SERVER2), aConfigGprs.iProtocolConfigOption.iDnsAddresses.iSecondaryDns);
    	    }
	    }
	else if (protocol.CompareF(KIp6) == 0)
	    {
	    // IPv6 settings
    	Notify()->ReadBool(TPtrC(GPRS_IP6_DNS_ADDR_FROM_SERVER), fromServer);
    	if (!fromServer)
    	    {
    	    Notify()->ReadDes(TPtrC(GPRS_IP6_NAME_SERVER1), aConfigGprs.iProtocolConfigOption.iDnsAddresses.iPrimaryDns);
    	    Notify()->ReadDes(TPtrC(GPRS_IP6_NAME_SERVER2), aConfigGprs.iProtocolConfigOption.iDnsAddresses.iSecondaryDns);
    	    }
	    }
	else
	    {
	    // Anything else is a serious misconfiguration
	    ASSERT(0);
	    }
	
	RetrieveGprsCompression(aConfigGprs.iPdpCompression);
	 __FLOG_1(_L8("Read GprsCompression [%d]"), aConfigGprs.iPdpCompression);

	RetrieveGprsAnonymousAccess(aConfigGprs.iAnonymousAccessReqd);
 	__FLOG_1(_L8("Read AnonymousAccess [%d]"), aConfigGprs.iAnonymousAccessReqd);

	TBuf<KCommsDbSvrMaxUserIdPassLength> readBuf;
	Notify()->ReadDes(TPtrC(SERVICE_IF_AUTH_NAME), readBuf);
	aConfigGprs.iProtocolConfigOption.iAuthInfo.iUsername.Copy(readBuf);
	readBuf.Zero();
	Notify()->ReadDes(TPtrC(SERVICE_IF_AUTH_PASS), readBuf);
	aConfigGprs.iProtocolConfigOption.iAuthInfo.iPassword.Copy(readBuf);
 	__FLOG_2(_L8("ReadDes SERVICE_IF_AUTH_NAME [%S] and SERVICE_IF_AUTH_PASS [%S]"), &aConfigGprs.iProtocolConfigOption.iAuthInfo.iUsername, 
			 &aConfigGprs.iProtocolConfigOption.iAuthInfo.iPassword);

	aConfigGprs.iUseEdge = EFalse;
	}

void CSpudMan::RetrieveGprsCompression(TUint& aCompression) const
	{
	aCompression = 0;
	TBool isCompression = EFalse;
	Notify()->ReadBool(TPtrC(GPRS_DATA_COMPRESSION), isCompression);
	if (isCompression)
		{
		aCompression |= RPacketContext::KPdpDataCompression;
		}

	isCompression = EFalse;
	Notify()->ReadBool(TPtrC(GPRS_HEADER_COMPRESSION), isCompression);
	if (isCompression)
		{
		aCompression |= RPacketContext::KPdpHeaderCompression;
		}
	}

void CSpudMan::RetrieveGprsAnonymousAccess(RPacketContext::TAnonymousAccess& aAnonymous) const
	{	
	TBool isAnonymous = EFalse;
	Notify()->ReadBool(TPtrC(GPRS_ANONYMOUS_ACCESS),isAnonymous);
	if (isAnonymous)
		aAnonymous = RPacketContext::ERequired;
	else
		aAnonymous = RPacketContext::ENotRequired;
	}



/**
Indicates whether QoS has been enabled or not.

@return ETrue if QoS has been enabled for this NIF.
*/
TBool CSpudMan::AreQoSEventsEnabled() const
	{
	return iQosEventHandler != NULL;
	}


/**
Returns pointer to BindMan.

@return Pointer to BindMan object
*/
CBindMan* CSpudMan::BindMan() const
	{
	return iBindMan;
	}

/**
Returns pointer to the MNifIfNotify object in NIFMAN.

@return Pointer to MNifIfNotify object
*/
MNifIfNotify* CSpudMan::Notify() const
	{
	ASSERT(iNotify);
	return iNotify;
	}

/**
Set default QoS parameters from the values in CommDb.

@param aQos QoS parameters filled in on exit
*/
void CSpudMan::ReadDefaultQoS(RPacketQoS::TQoSR99_R4Requested& aQos) const
	{
	TBuf<2*KCommsDbSvrMaxColumnNameLength+1> columnName;
	_LIT(KFormatText,"%s\\%s");

	columnName.Format(KFormatText,QOS_UMTS_R99_AND_ON_TABLE,GPRS_QOS_REQ_TRAFFIC_CLASS);
	Notify()->ReadInt (columnName, reinterpret_cast<TUint32&>(aQos.iReqTrafficClass));

	columnName.Format(KFormatText,QOS_UMTS_R99_AND_ON_TABLE,GPRS_QOS_MIN_TRAFFIC_CLASS);
	Notify()->ReadInt (columnName, reinterpret_cast<TUint32&>(aQos.iMinTrafficClass));
	
	columnName.Format(KFormatText,QOS_UMTS_R99_AND_ON_TABLE,GPRS_QOS_REQ_DELIVERY_ORDER);
	Notify()->ReadInt (columnName, reinterpret_cast<TUint32&>(aQos.iReqDeliveryOrderReqd));

	columnName.Format(KFormatText,QOS_UMTS_R99_AND_ON_TABLE,GPRS_QOS_MIN_DELIVERY_ORDER);
	Notify()->ReadInt (columnName, reinterpret_cast<TUint32&>(aQos.iMinDeliveryOrderReqd));
	
	columnName.Format(KFormatText,QOS_UMTS_R99_AND_ON_TABLE,GPRS_QOS_REQ_DELIVER_ERRONEOUS_SDU);
	Notify()->ReadInt (columnName, reinterpret_cast<TUint32&>(aQos.iReqDeliverErroneousSDU));

	columnName.Format(KFormatText,QOS_UMTS_R99_AND_ON_TABLE,GPRS_QOS_MIN_DELIVER_ERRONEOUS_SDU);
	Notify()->ReadInt (columnName, reinterpret_cast<TUint32&>(aQos.iMinDeliverErroneousSDU));
	
	columnName.Format(KFormatText,QOS_UMTS_R99_AND_ON_TABLE,GPRS_QOS_REQ_MAX_SDUSIZE);
	Notify()->ReadInt (columnName, reinterpret_cast<TUint32&>(aQos.iReqMaxSDUSize));

	columnName.Format(KFormatText,QOS_UMTS_R99_AND_ON_TABLE,GPRS_QOS_MIN_ACCEPTABLE_MAX_SDU_SIZE);
	Notify()->ReadInt (columnName, reinterpret_cast<TUint32&>(aQos.iMinAcceptableMaxSDUSize));
	
	columnName.Format(KFormatText,QOS_UMTS_R99_AND_ON_TABLE,GPRS_QOS_REQ_MAX_UPLINK_RATE);
	Notify()->ReadInt (columnName, reinterpret_cast<TUint32&>(aQos.iReqMaxRate.iUplinkRate));

	columnName.Format(KFormatText,QOS_UMTS_R99_AND_ON_TABLE,GPRS_QOS_REQ_MIN_UPLINK_RATE);
	Notify()->ReadInt (columnName, reinterpret_cast<TUint32&>(aQos.iMinAcceptableMaxRate.iUplinkRate));
	
	columnName.Format(KFormatText,QOS_UMTS_R99_AND_ON_TABLE,GPRS_QOS_REQ_MAX_DOWNLINK_RATE);
	Notify()->ReadInt (columnName, reinterpret_cast<TUint32&>(aQos.iReqMaxRate.iDownlinkRate));
	
	columnName.Format(KFormatText,QOS_UMTS_R99_AND_ON_TABLE,GPRS_QOS_REQ_MIN_DOWNLINK_RATE);
	Notify()->ReadInt (columnName, reinterpret_cast<TUint32&>(aQos.iMinAcceptableMaxRate.iDownlinkRate));
	
	columnName.Format(KFormatText,QOS_UMTS_R99_AND_ON_TABLE,GPRS_QOS_REQ_BER);
	Notify()->ReadInt (columnName, reinterpret_cast<TUint32&>(aQos.iReqBER));
	
	columnName.Format(KFormatText,QOS_UMTS_R99_AND_ON_TABLE,GPRS_QOS_MAX_BER);
	Notify()->ReadInt (columnName, reinterpret_cast<TUint32&>(aQos.iMaxBER));
	
	columnName.Format(KFormatText,QOS_UMTS_R99_AND_ON_TABLE,GPRS_QOS_REQ_SDU_ERROR_RATIO);
	Notify()->ReadInt (columnName, reinterpret_cast<TUint32&>(aQos.iReqSDUErrorRatio));
	
	columnName.Format(KFormatText,QOS_UMTS_R99_AND_ON_TABLE,GPRS_QOS_MAX_SDU_ERROR_RATIO);
	Notify()->ReadInt (columnName, reinterpret_cast<TUint32&>(aQos.iMaxSDUErrorRatio));

	columnName.Format(KFormatText,QOS_UMTS_R99_AND_ON_TABLE,GPRS_QOS_REQ_TRAFFIC_HANDLING_PRIORITY);
	Notify()->ReadInt (columnName, reinterpret_cast<TUint32&>(aQos.iReqTrafficHandlingPriority));
	
	columnName.Format(KFormatText,QOS_UMTS_R99_AND_ON_TABLE,GPRS_QOS_MIN_TRAFFIC_HANDLING_PRIORITY);
	Notify()->ReadInt (columnName, reinterpret_cast<TUint32&>(aQos.iMinTrafficHandlingPriority));

	columnName.Format(KFormatText,QOS_UMTS_R99_AND_ON_TABLE,GPRS_QOS_REQ_TRANSFER_DELAY);
	Notify()->ReadInt (columnName, reinterpret_cast<TUint32&>(aQos.iReqTransferDelay));
	
	columnName.Format(KFormatText,QOS_UMTS_R99_AND_ON_TABLE,GPRS_QOS_MAX_TRANSFER_DELAY);
	Notify()->ReadInt (columnName, reinterpret_cast<TUint32&>(aQos.iMaxTransferDelay));

	columnName.Format(KFormatText,QOS_UMTS_R99_AND_ON_TABLE,GPRS_QOS_REQ_GUARANTEED_UPLINK_RATE);
	Notify()->ReadInt (columnName, reinterpret_cast<TUint32&>(aQos.iReqGuaranteedRate.iUplinkRate));

	columnName.Format(KFormatText,QOS_UMTS_R99_AND_ON_TABLE,GPRS_QOS_MIN_GUARANTEED_UPLINK_RATE);
	Notify()->ReadInt (columnName, reinterpret_cast<TUint32&>(aQos.iMinGuaranteedRate.iUplinkRate));

	columnName.Format(KFormatText,QOS_UMTS_R99_AND_ON_TABLE,GPRS_QOS_REQ_GUARANTEED_DOWNLINK_RATE);
	Notify()->ReadInt (columnName, reinterpret_cast<TUint32&>(aQos.iReqGuaranteedRate.iDownlinkRate));

	columnName.Format(KFormatText,QOS_UMTS_R99_AND_ON_TABLE,GPRS_QOS_MIN_GUARANTEED_DOWNLINK_RATE);
	Notify()->ReadInt (columnName, reinterpret_cast<TUint32&>(aQos.iMinGuaranteedRate.iDownlinkRate));
	}

#ifdef SYMBIAN_NETWORKING_UMTSR5
/**
Set default R5 QoS parameters from the values in CommDb.

@param aQos R5 QoS parameters filled in on exit
*/
void CSpudMan::ReadDefaultR5QoS(RPacketQoS::TQoSR5Requested& aQos) const
	{
	ReadDefaultQoS(aQos);
	
	TBuf<2*KCommsDbSvrMaxColumnNameLength+2> columnName;
	_LIT(KFormatText,"%s\\%s");
	columnName.Format(KFormatText,QOS_UMTS_R99_AND_ON_TABLE,GPRS_QOS_SIGNALLING_INDICATION);
	Notify()->ReadBool (columnName, aQos.iSignallingIndication);
	columnName.Format(KFormatText,QOS_UMTS_R99_AND_ON_TABLE,GPRS_QOS_SOURCE_STATISTICS_DESCRIPTOR);
	Notify()->ReadInt(columnName, reinterpret_cast<TUint32&>(aQos.iSourceStatisticsDescriptor));
	}
#endif
// SYMBIAN_NETWORKING_UMTSR5


/**
Reads TSY name from CommDb.

@param aTsyName TSY name filled in on exit
*/
void CSpudMan::ReadTsyName(TName& aTsyName) const
	{
	TBuf<2*KCommsDbSvrMaxColumnNameLength+1> columnName;
	_LIT(KFormatText,"%s\\%s");
	columnName.Format(KFormatText,MODEM_BEARER,MODEM_TSY_NAME);
	Notify()->ReadDes(columnName, aTsyName); // ignore error
	}

/**
Sets the error code to use for termination, unless it is already set.

@param aError Error code to use when notifying NIFMAN
*/
void CSpudMan::SetTerminateError(TInt aError)
	{
	if (iTerminateError == KErrNone)
		{
		iTerminateError = aError;
		__FLOG_1(_L("Set SPUD termination error to [%d]"), aError);
		
		// There is no specific context associated with this termination
		// So prevent a later overwrite of the error by the ETel error
		if (iETelTerminateError == KErrNone)
			{
			iETelTerminateError = aError;
			}
		}
	}

/**
Sets the error code to use for termination, unless it is already set.

@param aContextId context ID
@param aError Error code to use when notifying NIFMAN
*/
void CSpudMan::SetTerminateError(TContextId aContextId, TInt aError)
	{
	if (iTerminateError == KErrNone)
		{
		iTerminateError = aError;
		
		// Now check to see if ETel was the originator of this error
		if (iETelTerminateError == KErrNone)
			{
			// ask for the last ETel error received - ignore return code
			iPdpFsmInterface.GetLastErrorCause(aContextId, iETelTerminateError);
			}

		if (iETelTerminateError != KErrNone)
			{
			// Overwrite with the most likely original error code!
			iTerminateError = iETelTerminateError;
			}
	
		__FLOG_1(_L("Set SPUD termination error to [%d]"), iTerminateError);
		}
		
	}

//*****************************************************************************
// Events receivers
//*****************************************************************************

/**
Receives events from SpudFsm.

@param aContextId context ID
@param aEvent event ID
@param aParam optional parameter to event
*/
void CSpudMan::Input(TContextId aContextId, TInt aEvent, TInt aParam)
	{
	__FLOG_4(_L("SpudMan::Input: SpudFsm event %S(%d) param[%d] context[%d]"), SpudFsmEventToText(aEvent), aEvent, aParam, aContextId);
	ASSERT(aContextId == KAllContexts || (aContextId >=0 && aContextId < KMaxPdpContexts));
	
	switch (aEvent)
		{
	case KPrimaryContextCreated:
		{
		HandlePrimaryContextCreatedEvent(aContextId, aParam);
		break;
		}
	
	case KContextDeleteEvent:
		{
		HandleContextDeleteEvent(aContextId, aParam);
		break;
		}

	case KSecondaryContextCreated:
		{
		HandleSecondaryContextCreatedEvent(aContextId, aParam);
		break;
		}

	case KContextActivateEvent:
		{
		HandleContextActivateEvent(aContextId, aParam);
		break;
		}

	case KContextQoSSetEvent:
		{
		HandleContextQoSSetEvent(aContextId, aParam);
		break;
		}

	case KContextTFTModifiedEvent:
		{
		HandleContextTFTModifiedEvent(aContextId, aParam);
		break;
		}

	case KGetNegQoSEvent:
		{
		HandleGetNegQoSEvent(aContextId, aParam);
		break;
		}

	case KContextModifyActiveEvent:
		{
		HandleContextModifyActiveEvent(aContextId, aParam);
		break;
		}

	case KNetworkStatusEvent:
		{
		HandleNetworkStatusEvent();
		break;
		}

	case KContextParametersChangeEvent:
		{
		HandleContextParametersChangeEvent(aContextId, aParam);
        break;
		}

	case KContextBlockedEvent:
		{
		HandleContextBlockedEvent(aContextId);
		break;
		}

	case KContextUnblockedEvent:
		{
		HandleContextUnblockedEvent(aContextId);
		break;
		}


   case KPdpFsmShuttingDown:
      {
      __FLOG_0(_L("UmtsGprsSCPR has shutdown the PDP Fsm Interface"));
      iPdpFsmInterface.Close();
      break;
      }

	default:
		__FLOG_1(_L("Unhandled event %d"), aEvent);
		ASSERT(EFalse);
		break;
		}
	}
	
void CSpudMan::InitPdpFsmInterfaceL()
	{
	class XAssociatedNifConnectionProviderQuery : public MFactoryQuery
	/**	Finds the connection provider associated with the specified Nif.
	@internalTechnology
	*/
		{
	public:
		XAssociatedNifConnectionProviderQuery( const TDesC& aName, ::TMetaDes8& aNameBuffer ) : iNifName( aName ), iNifNameBuffer( aNameBuffer )
			{
			}

		virtual TMatchResult Match( TFactoryObjectInfo& aObjectInfo )
			{
			TConnInterfaceName& name = *reinterpret_cast<TConnInterfaceName*>( const_cast<TUint8*>( iNifNameBuffer.iDes->Ptr() ) );
			name.iIndex = 1;

			TInt err = KErrNone;
			TMatchResult result = EContinue;
			
			// Check each of the connection provider interfaces' names to see if it
			// is associated with this NIF.
			while( ETrue )
				{
		        TRAP( err, static_cast<CConnectionProviderBase*>( aObjectInfo.iInfo.iFactoryObject )->ControlL(KCOLProvider, KConnGetInterfaceName, iNifNameBuffer, NULL) );
		        
		        if( err == KErrNone )
		        	{
		        	if( name.iName == iNifName )
			        	{
				       	result = EMatch;
				       	
				       	break;
			        	}
			       	}
			    else
			    	{
			    	break;
			    	}

				name.iIndex ++;
				}
	        	
	        return result;
			}
			
	private:
		const TDesC& iNifName;
		::TMetaDes8& iNifNameBuffer;
		};

	const TUint KShimConnectionProviderFactoryId = 0x10207104; //the same as CSubConnectionProviderFactoryShim

	TSockManData* sockManData = SockManGlobals::Get();
	ASSERT(sockManData);

	CConnectionFactoryContainer* connectionFactories = sockManData->iConnectionFactories;
	ASSERT(connectionFactories);

	CConnectionProviderFactoryBase *factory = connectionFactories->FindFactory(KShimConnectionProviderFactoryId);
	if(!factory)
		{
		User::Leave( KErrNotFound );
		}
		
	CSpudMux* mux = iBindMan->SpudMux();

	// Get the name of our SPUDMUX interface - it uniquely identifies this SPUD object assembly.
	TNifIfInfo info;
	mux->Info( info );

	// Create a buffer to hold the name of each interface we check to see if the interface is our SPUDMUX.
	TPckgBuf<TConnInterfaceName> name;
	TPtrC8 desC( name );
	::TMetaDes8* des = ::TMetaDes8::NewL( &desC );

	XAssociatedNifConnectionProviderQuery connProviderQuery( info.iName, *des );
	
	// Find the connection provider associated with this NIF.
	CConnectionProviderBase* connectionProvider = factory->FindProvider(connProviderQuery);
	delete des;
	if(!connectionProvider)
		{
		User::Leave( KErrNotFound );
		}

	CSubConnectionFactoryContainer* subConnectionFactories = sockManData->iSubConnectionFactories;
	ASSERT(subConnectionFactories);

	TDataClientQuery subConnProviderQuery(connectionProvider, ESubConnPlane, RSubConnection::EAttachToDefault);

	// Find the default subconnection provider.
	CSubConnectionProviderBase* subConnProvider = subConnectionFactories->FindOrCreateProviderL(connectionProvider->CanDoSubConnection(RSubConnection::EAttachToDefault), subConnProviderQuery);
	if(!subConnProvider || subConnProvider->Factory().Id() != KUmtsGprsSubConnectionProviderFactoryId)
		{
		User::Leave( KErrNotFound );
		}

	// Initialise the PDP FSM interface.
	TPckg<CNifIfBase*> nifPckg(mux);
	User::LeaveIfError(subConnProvider->Control(KSOLInterface, KInitialisePdpFsm, nifPckg));
	}

void CSpudMan::HandlePrimaryContextCreatedEvent(TContextId aContextId, TInt aError)
	{
		
	// Save the contextId for later
	iPrimaryContextId = aContextId;
	
	// Primary context has been created; Start the lower NIF
	CSpudBinderRef* ref = NULL;
	TRAPD(rc, ref = iBindMan->GetRefL(aContextId));
	__FLOG_2(_L("Got lower NIF binder for context[%d] with error[%d]"), aContextId, rc);
	if (rc == KErrNone)
		{
		// Make sure context was created successfully
		rc = aError;
		ASSERT(ref->IsBound());
		__FLOG_3(_L("context %d is in state %S(%d)"), aContextId, SpudStateToText(ref->State()), ref->State());
		}

	if (KErrNone == rc) // Context created successfully.
		{
			// Get the details of the P-CSCF 
		RPacketContext::TContextConfigGPRS config;
		
		if (iPdpFsmInterface.Get(aContextId, config) == KErrNone)
			{
			TRAP(rc, SetSipServerAddrL(config.iProtocolConfigOption););
				{
				__FLOG_1(_L("CSpudMan::HandlePrimaryContextCreatedEvent -Error occurred extracting the sip server address: %d"),rc);
				//rc is now non zero, therefore should fail gracefully
				}	
#ifdef SYMBIAN_NETWORKING_UMTSR5
			// Since the value or IMCN Signalling flag also comes in PCO IE buffer along with the SipAddress, we
			// need to extract the value here and set it to Parameter Bundle of SCPR
				TBool imcn = EFalse;		
				TRAPD(err,imcn=GetIMCNSignallingFlagPcoL(config.iProtocolConfigOption););
				__FLOG_1(_L("CSpudMan::GetIMCNSignallingFlagPcoL - returns eror=%d"),err);
				if (err == KErrNone)
					{
					iPdpFsmInterface.SetIMCNSignalling(imcn); //imcn contains ETrue or Efalse
#ifdef __FLOG_ACTIVE
					if (imcn)
						{
						__FLOG_0(_L("CSpudMan::HandlePrimaryContextCreatedEvent - Network ACCEPTS the request for dedicated IMCN Signalling Flag "));
						}
					else
						{
						__FLOG_0(_L("CSpudMan::HandlePrimaryContextCreatedEvent - Network REJECTS the request for dedicated IMCN Signalling Flag "));				
						}
#endif 
					}
					else
					{
					
#ifdef __FLOG_ACTIVE
					__FLOG_1(_L("CSpudMan::HandlePrimaryContextCreatedEvent -Leave Error occurred  %d"),err);
#endif
					}			
#endif // SYMBIAN_NETWORKING_UMTSR5
			}
		}
		
	if (KErrNone == rc) // Sip server address retrieved
		{
		ASSERT(ref->State() == ESpudCreatingPrimary);
	    ref->SetState(ESpudStartingPrimary);
	    // Start the lower NIF and wait for the LinkLayerUp call
		rc = ref->NifLink()->Start(); 
		if(KErrNone == rc)
			{
			ref->SetState(ESpudStartingPrimaryLowerNif);
			}
		// Lower NIF may still report failure to Start by signalling LinkLayerDown with error.
		// E.G. PPP negotiation may fail.
		__FLOG_2(_L("Lower NIF for Primary Context[%d] started with error[%d]."), aContextId, rc);
		}

		
	// Catch-all error handling: Could not create context, or could not start lower NIF.	
	if(KErrNone != rc) 
		{			
		SetTerminateError(rc);	
		
	    SendPrimaryContextCreated(aContextId, rc); // Notify GUQoS of error

 		if(ESpudStartingPrimary == ref->State()) // Context created 
 			{
 			ref->SetState(ESpudWaitLinkDown); // We've just notified GUQoS.
 			// Delete context via SpudFsm
			__FLOG_2(_L("Failed to start lower NIF for Primary PDP context[%d] due to error[%d]. Deleting Primary via SpudFsm."), aContextId, rc);
			rc = iPdpFsmInterface.Input(aContextId, EContextDelete);
			ASSERT(rc == KErrNone);
 			}
 		else 	// Context was not created, else we would not be here. There is nothing to stop and delete.
 			{
 			__FLOG_1(_L("Failed to create Primary context[%d]. Spud will shut down."), aContextId);
			
 			if (AreQoSEventsEnabled())
				{
				// At this point we know that SPUD is about to shut down, because we are
				// deleting the last context. We want to indicate to the upper layers early that
				// data can no longer be sent to SPUD.
									
				// Tell GUQoS to stop bothering SPUD.
				// GUQoS returns the favour by turning off the NIF events within this very call.
				// This means we won't send KNetworkInterfaceDown, even if we try.
		   		// *********************************************************************************************
				// N.B.: "DEF055691 	GUQoS crashes at shutdown": This defect results in GUQoS crash if the stack
    			// closes flows after the NIF proxy was deleted by GUQoS, as a result of KNetworkInterfaceDown event.
    			// As a temporary workaround, this call is disabled, and KNetworkInterfaceDown event is signalled only
    			// from the destructor. Once this defect is fixed, the following line must be uncommented: 
    			// SendNetworkStatusEvent(KNetworkInterfaceDown, RPacketService::EStatusUnattached);
				//**********************************************************************************************
		   		}
			// We are about to delete the primary context NIF: this will notify Nifman that SPUD is finished.
 			DisposeOfBinder(ref);
 			}
 		}		
	}

void CSpudMan::HandleContextDeleteEvent(TContextId aContextId, TInt aError)
	{
	aError = aError; // suppress compiler warning
	// Ignore error on delete--nothing we can do, anyway
	// For the upper layers, we treat this event as KErrDisconnected, because the network has torn down an 
	// existing context.
	CSpudBinderRef* ref = NULL;
	TRAPD(rc, ref = iBindMan->GetRefL(aContextId));
	__ASSERT_ALWAYS(rc == KErrNone, Panic());
	ASSERT(ref->IsBound());
	__FLOG_4(_L("Network deleted context %d, in state %S(%d). Deletion error=%d"),
		aContextId, SpudStateToText(ref->State()), ref->State(), aError);
	
	// Are we about to shut down after this context is deleted?
	TBool shutdownStarted(BindMan()->IsLastContext(aContextId));
	// We must determine this before we make any state transitions. But we can act on this
	// only after we've done all GUQoS notifications to make sure shutdown is graceful.
	
	switch (ref->State()) 
		{
	case ESpudUp:
	case ESpudFlowOff:
	case ESpudSuspended:
	case ESpudFlowOffAndSuspended:
		ref->SetState(ESpudWaitLinkDown);
		SendContextDeleteEvent(aContextId);
		ref->NifLink()->Stop(KErrConnectionTerminated, MNifIfNotify::EDisconnect);
        break;
        
    case ESpudStartingPrimaryLowerNif:
    	ref->SetState(ESpudWaitLinkDown);
    	SendPrimaryContextCreated(aContextId, KErrDisconnected);
    	SendContextDeleteEvent(aContextId);
    	ref->NifLink()->Stop(KErrConnectionTerminated, MNifIfNotify::EDisconnect);
        break;
        	        
	case ESpudGettingNegQoS: // Context was activated. It is assumed that QoS retrieval would be cancelled by deletion.
	case ESpudStartingSecondaryLowerNif:
		ref->SetState(ESpudStartingSecondary);
		FillInContextConfig(iTempContextConfig, aContextId);
		SendContextActivateEvent(aContextId, iTempContextConfig, KErrDisconnected);
		SendContextDeleteEvent(aContextId);
		ref->NifLink()->Stop(KErrConnectionTerminated, MNifIfNotify::EDisconnect);
        break;
		
	case ESpudStartingPrimary: 
		// this should never happen
		SendPrimaryContextCreated(aContextId, KErrDisconnected);
		SendContextDeleteEvent(aContextId);
		DisposeOfBinder(ref);
		break;
	
	case ESpudStartingSecondary:
		// This should never happen, and may cause problems if it does because there could be
		// another outstanding request from GUQoS that never gets completed.
		__FLOG_0(_L("Network should not have deleted context in ESpudStartingSecondary state!"));
		// Fall through and treat the same as ESpudLinkDown
		
	case ESpudLinkDown:
		SendContextDeleteEvent(aContextId);
		DisposeOfBinder(ref);
		break;
	
	case ESpudWaitLinkDown:	
		DisposeOfBinder(ref);
		break;
		
	
	default:
		__FLOG_2(_L("Unexpected state %S(%d)"), SpudStateToText(ref->State()), ref->State());
		ASSERT(EFalse);
		break;
		}
	
	if(shutdownStarted) // Additional steps if we are shutting down as as result of this deletion.
		{
		SetTerminateError(KErrDisconnected); 
		// We don't reuse errorForGuqos, because we may want to keep Nifman errors and GUQoS errors separate.	
		if (AreQoSEventsEnabled())
			{
			// At this point we know that SPUD is about to shut down, because the last context 
			// was just deleted. We want to indicate to the upper layers early that
			// data can no longer be sent to SPUD.
								
			// Tell GUQoS to stop bothering SPUD.
			// GUQoS returns the favour by turning off the NIF events within this very call.
			// This means we send KNetworkInterfaceDown only once.
			// *******************************************************************************************
			// N.B.: "DEF055691 	GUQoS crashes at shutdown": This defect results in GUQoS crash if the stack
    		// closes flows after the NIF proxy was deleted by GUQoS, as a result of KNetworkInterfaceDown event.
    		// As a temporary workaround, this call is disabled, and KNetworkInterfaceDown event is signalled only
    		// from the destructor. Once this defect is fixed, the following line must be uncommented: 
    		// SendNetworkStatusEvent(KNetworkInterfaceDown, RPacketService::EStatusUnattached);
			//**********************************************************************************************
			}	
		}	
	}

void CSpudMan::HandleSecondaryContextCreatedEvent(TContextId aContextId, TInt aError)
	{
	CSpudBinderRef* ref = NULL;
	TRAPD(rc, ref = iBindMan->GetRefL(aContextId));
	if (rc == KErrNone)
		{
		ASSERT(ref->IsBound());
		__FLOG_4(_L("Network created secondary context[%d] in state %S(%d). Creation error=%d "),aContextId, SpudStateToText(ref->State()), ref->State(), aError);
		if (ref->State() != ESpudStartingSecondary)
			{
			__FLOG_3(_L("KSecondaryContextCreated context %d is in unexpected state %S(%d)"),
			         aContextId, SpudStateToText(ref->State()), ref->State());
			}
		ref = ref;	// Eliminate compiler warning in release builds
		
		// Make sure context was created successfully
		rc = aError;
		if(KErrNone != rc) // Creation failed: we don't have the context, only the binder. Delete it.
			{
			DisposeOfBinder(ref);
			}
		}
	SendSecondaryContextCreated(aContextId, rc);
	}

void CSpudMan::HandleContextActivateEvent(TContextId aContextId, TInt aError)
	{
	CSpudBinderRef* ref = NULL;
	TRAPD(rc, ref = iBindMan->GetRefL(aContextId));
	if (rc == KErrNone)
		{
		// Make sure context was activated successfully
		rc = aError;
	
		ASSERT(ref->IsBound());
		__FLOG_3(_L("context %d is in state %S(%d)"), aContextId, SpudStateToText(ref->State()), ref->State());
		}

	if (rc == KErrNone)
		{
		ASSERT(ref->State() == ESpudStartingSecondary);
		// Start the lower NIF
		rc = ref->NifLink()->Start(); 
		// Even if we get KErrNone here, we may still get LinkLayerDown with error later.
		__FLOG_2(_L("KContextActivateEvent: Start on lower NIF returned for Context[%d] returned error[%d]"),aContextId, rc);
		}
	
	if(KErrNone == rc) // Start OK.
		{
		
		ref->SetState(ESpudStartingSecondaryLowerNif);
		}
	else
		{
		// Error activating PDP context
		FillInContextConfig(iTempContextConfig, aContextId);
		SendContextActivateEvent(aContextId, iTempContextConfig, rc);
		}
	}

void CSpudMan::HandleContextQoSSetEvent(TContextId aContextId, TInt aError)
	{
	// Notify GUQoS of success or failure
    SendContextQoSSetEvent(aContextId, aError);
	}

void CSpudMan::HandleContextTFTModifiedEvent(TContextId aContextId, TInt aError)
	{
	CSpudBinderRef* ref = NULL;
	TRAPD(rc, ref = iBindMan->GetRefL(aContextId));
	__ASSERT_ALWAYS(rc == KErrNone, Panic());
	ASSERT(ref->IsBound());
	__FLOG_3(_L("context %d is in state %S(%d)"), aContextId, SpudStateToText(ref->State()), ref->State());
	ref = ref;	// Eliminate compiler warning in release builds

	// Notify GUQoS of success or failure
	TTFTOperationCode opCode;
    iPdpFsmInterface.Get(aContextId, opCode);
    SendContextTFTModifiedEvent(aContextId, opCode, aError);
	}

void CSpudMan::HandleGetNegQoSEvent(TContextId aContextId, TInt aError)
	{
	CSpudBinderRef* ref = NULL;
	TRAPD(rc, ref = iBindMan->GetRefL(aContextId));
	__ASSERT_ALWAYS(rc == KErrNone, Panic());
	
	ASSERT(ref->IsBound());
	__FLOG_4(_L("CSpudMan: Retrieved Negotiated QoS, error=%d: context %d is in state %S(%d)"), aError, aContextId, SpudStateToText(ref->State()), ref->State());
	if (ref->State() == ESpudGettingNegQoS)
		{
		// Negotiated QoS retrieved successfully. At this point the ctx is up both on control and data paths.
		if(KErrNone == aError) 
			{
			// Context is now fully up. Notify GUQoS.
			ref->SetState(ESpudUp);
			// Notify GUQoS of success or failure
			FillInContextConfig(iTempContextConfig, aContextId);
    		SendContextActivateEvent(aContextId, iTempContextConfig, aError);
			
			if(ref->State() == ESpudUp) // GUQoS can delete the context from the activation notification.
				{
				BindMan()->SpudProtocol()->DoStartSending();
				SendContextUnblockedEvent(aContextId);	
				}
			}
		// If the QoS could not be retrieved, we remain in ESpudGettingNegQoS and wait for 
		// the network to delete the context. The rest would be handled from the deletion event.
		
		// N.B. CRITICAL ASSUMPTION: 
		// The network / TSY will delete the ctx if the negotiated QoS could not be retrieved.
		// (The idea is that if QoS negotiation is errored out, then everything is errored out.
		// So we either do not get to this point at all, or will be errored out soon after this handler completes.)
		// This leaves the issue of internal ETel/TSY errors such as OOM conditions.)
		// At this point Spud is idle (no requests outstanding), so nothing will drive us
		// forward to clean up this failure. GUQoS does NOT time out on PDP context creation failure.
		}
	else
		{
		// If we are not getting negotiated QoS, but we receive this notification, then:
		// - ETel has sent us a stray notification totally uncalled for, i.e. it's a bug in ETel or more likely a TSY.
		if(KErrNone == aError)
			{
			__FLOG(_L("WARNING! Negotiated QoS retrieval completed successfully, but totally out of order! See the comments in the code.)"));
			}
		// We don't assert, because it can be a race condition outside of ETel's control:
		// - ETel, lower NIF, GUQoS, Nifman has errored the context out (e.g. ctx deleted by network), so there was a 
		// state transition as a result of that, and now the QoS retrieval has completed after that with KErrNone.
		}
	}


void CSpudMan::HandleContextModifyActiveEvent(TContextId aContextId, TInt aError)
	{
	// Notify GUQoS of success or failure
	FillInContextConfig(iTempContextConfig, aContextId);
    SendContextModifyActiveEvent(aContextId, iTempContextConfig, aError);
	}

void CSpudMan::HandleNetworkStatusEvent()
	{
	// This is a network status change
	RPacketService::TStatus status;
	iPdpFsmInterface.Get(status);
	
	// Assume that anything other than EStatusUnattached means the network
	// is still up, so just ignore this event.
	if (status == RPacketService::EStatusUnattached)
		{
		// Notify GUQoS
		SendNetworkStatusEvent(KNetworkConnectionLost, status);
		}
	else
		{
		__FLOG_1(_L("Ignoring KNetworkStatusEvent with status %d (NOT EStatusUnattached)"),
				 status);
		}
	}

void CSpudMan::HandleContextParametersChangeEvent(TContextId aContextId, TInt aError)
	{
	// This is a status change on an individual context
	CSpudBinderRef* ref = NULL;
	TRAPD(rc, ref = iBindMan->GetRefL(aContextId));
	__ASSERT_ALWAYS(rc == KErrNone, Panic());
	ASSERT(ref->IsBound());
	__FLOG_3(_L("context %d is in state %S(%d)"), aContextId, SpudStateToText(ref->State()), ref->State());
	if (ref->State() == ESpudGettingNegQoS || ref->State() == ESpudUp || ref->State() == ESpudFlowOff || ref->State() == ESpudSuspended || ref->State() == ESpudFlowOffAndSuspended)
		{
		// Only pass on context changes while the context is up
		FillInContextConfig(iTempContextConfig, aContextId);
		SendContextParametersChangeEvent(aContextId, iTempContextConfig, aError);
		}
	else
		{
		__FLOG_3(_L("KContextParametersChangeEvent ignored on context %d because of nonoperational state %S(%d)"),
				 aContextId, SpudStateToText(ref->State()), ref->State());
        SetTerminateError(aContextId, aError);
		}
	}

void CSpudMan::HandleContextBlockedEvent(TContextId aContextId)
	{
	// Context is suspended (see StopSending)
	CSpudBinderRef* ref = NULL;
	TRAPD(rc, ref = iBindMan->GetRefL(aContextId));
	__ASSERT_ALWAYS(rc == KErrNone, Panic());
	ASSERT(ref->IsBound());
	__FLOG_3(_L("context %d is in state %S(%d)"), aContextId, SpudStateToText(ref->State()), ref->State());
	switch (ref->State())
		{
	case ESpudUp:
		{
		ref->SetState(ESpudSuspended);
		if (AreQoSEventsEnabled())
			{
			SendContextBlockedEvent(aContextId);
			}
			
		// Send an IfProgress to Nifman so that RConnection::ProgressNotification reports the blockage etc.
		Notify()->IfProgress(KDataTransferTemporarilyBlocked, KErrNone);
		break;
		}

	case ESpudFlowOff:
		ref->SetState(ESpudFlowOffAndSuspended);
			
		// Send an IfProgress to Nifman so that RConnection::ProgressNotification reports the blockage etc.
		Notify()->IfProgress(KDataTransferTemporarilyBlocked, KErrNone);
		break;

	case ESpudSuspended:
	case ESpudFlowOffAndSuspended:
		// Ignore this since we're already suspended
		break;

	default:
		// Ignore this since we're still starting up or shutting down
		__FLOG_1(_L("Can't send blocked event now on context %d"), aContextId);
		break;
		}
		
	// TODO: Probably need to send IfProgress here where aStage==KDataTransferTemporarilyBlocked
	// or Notification with aEvent=EAgentToNifEventTypeDisableTimers
	//
	// NOTE: The IfProgress has now been implemented so that callers of RConnection::ProgressNotification
	// expecting to receive KDataTransferTemporarilyBlocked actually get it if the PDP context is
	// suspended (details of problem in INC107930).
	}

void CSpudMan::HandleContextUnblockedEvent(TContextId aContextId)
	{
	// Context is suspended (see StartSending)
	CSpudBinderRef* ref = NULL;
	TRAPD(rc, ref = iBindMan->GetRefL(aContextId));
	__ASSERT_ALWAYS(rc == KErrNone, Panic());
	ASSERT(ref->IsBound());
	__FLOG_3(_L("context %d is in state %S(%d)"), aContextId, SpudStateToText(ref->State()), ref->State());
	switch (ref->State())
		{
	case ESpudUp:
	case ESpudFlowOff:
		// Ignore this since we're already in an unsuspended state
		break;

	case ESpudSuspended:
		{
		ref->SetState(ESpudUp);

		BindMan()->SpudProtocol()->DoStartSending();

		if (AreQoSEventsEnabled())
			{
			SendContextUnblockedEvent(aContextId);
			}
		Notify()->IfProgress(KLinkLayerOpen, KErrNone);
		break;
		}

	case ESpudFlowOffAndSuspended:
		{
		ref->SetState(ESpudFlowOff);
		Notify()->IfProgress(KLinkLayerOpen, KErrNone);
		break;
		}

	default:
		// Ignore this since we're still starting up or shutting down
		__FLOG_1(_L("Can't send unblocked event now on context %d; this may cause problems"), aContextId);
		break;
		}
	}

/**
Receives event from GUQoS.

@param aName event identification
@param aOption optional data associated with event
@return error code
*/
TInt CSpudMan::GuqosInput(TUint aName, TDes8& aOption)
	{
	__FLOG_2(_L("SpudMan::GuqosInput: GUQoS event %S(%d)"), SpudGuQoSEventToText(aName), aName);
	switch (aName)
		{
	case KSoIfControllerPlugIn:
        {
        // Indicate that we want GUQoS
        TSoIfControllerInfo& opt = *reinterpret_cast<TSoIfControllerInfo*>(const_cast<TUint8*>(aOption.Ptr()));
        _LIT(KQosPlugInName, "guqos");
        opt.iPlugIn = KQosPlugInName;
        opt.iProtocolId = KQosPlugInProtocolId;
        return KErrNone;
        }

    case KRegisterEventHandler:
        {
        // GUQoS has passed a pointer to its event handler
        const TEvent *handler = reinterpret_cast<const TEvent *>(aOption.Ptr());
        iQosEventHandler = static_cast<MNifEvent *>(handler->iEvent);
        ASSERT(iQosEventHandler);
        return KErrNone;
        }

	case KContextSetEvents:
		{
        if (!AreQoSEventsEnabled())
        	{
        	// Event handler must be registered first
            return KErrGeneral;
        	}
        const TBool* eventsEnabledPtr = reinterpret_cast<const TBool *>(aOption.Ptr());
        ASSERT(eventsEnabledPtr);
        iQosEventsEnabled = *eventsEnabledPtr;

        // Has a primary PDP context has already been created?
       	CSpudBinderRef* ref = NULL;
		TRAPD(rc, ref = BindMan()->GetAnyRefL());
		const TBool havePrimary(rc == KErrNone && (ref->State() == ESpudUp || ref->State() == ESpudFlowOff || ref->State() == ESpudSuspended || ref->State() == ESpudFlowOffAndSuspended));

        if (iQosEventsEnabled && havePrimary)
        	{
        	// iPrimaryContextId == KPrimaryContextId == 0 at startup
            SendPrimaryContextCreated(iPrimaryContextId, KErrNone);
            }
        return KErrNone;
		}

	case KNifSetDefaultQoS:
		{
         ASSERT(aOption.Ptr());
         const TContextParameters& opt = *reinterpret_cast<const TContextParameters*>(aOption.Ptr());

         if (!iPdpFsmInterface.IsInitialised()) 
            {
            __FLOG_0(_L("CSpudMan::GuqosInput: iPdpFsmInterface not initialised, parking KNifSetDefaultQoS."));
            
            if (iParkedDefaultQoS == NULL)
               {
               TRAPD(err, iParkedDefaultQoS = HBufC8::NewL (sizeof (TContextParameters)));
               if (err != KErrNone)
                  {
                  __FLOG_0(_L("CSpudMan::GuqosInput: Failed to park default QoS."));
                  return err;
                  }
               }
               
            iParkedDefaultQoS->Des().Copy (aOption);

            return KErrNone;
            }
         
       	CSpudBinderRef* ref = NULL;
       	// Lower NIF for primary context has already been loaded by factory
		TRAPD(rc, ref = BindMan()->GetRefL(iPrimaryContextId));
		__ASSERT_ALWAYS(rc == KErrNone, Panic());
		ASSERT(ref->IsBound());
		
		ASSERT(ref->State() == ESpudInactive);
        ref->SetState(ESpudHaveQos);
        	
#ifdef SYMBIAN_NETWORKING_UMTSR5
		// Store R5 QoS parameters
			RPacketQoS::TQoSR5Requested qos;
#else
		// Store QoS parameters
			RPacketQoS::TQoSR99_R4Requested qos;
#endif 
// SYMBIAN_NETWORKING_UMTSR5
		
		opt.iContextConfig.GetUMTSQoSReq(qos); 
		rc = iPdpFsmInterface.Set(iPrimaryContextId, qos);	

        if (rc != KErrNone)
			{
			__FLOG_1(_L("Setting default QoS on primary context failed with error [%d]. SPUD will shut down."), rc);
			SetTerminateError(iPrimaryContextId, rc); 
			
			SendPrimaryContextCreated(iPrimaryContextId, rc); // Notify GUQoS of error
			
			// At this point we know that SPUD is about to shut down, because we could not bring the 
	 		// primary context UP.
	
			//**********************************************************************************************
			// Tell GUQoS to stop bothering SPUD.
			// GUQoS returns the favour by turning off the NIF events within this very call.
	        // N.B.: "DEF055691 	GUQoS crashes at shutdown": This defect results in GUQoS crash if the stack
	        // closes flows after the NIF proxy was deleted by GUQoS, as a result of KNetworkInterfaceDown event.
	        // As a temporary workaround, this call is disabled, and KNetworkInterfaceDown event is signalled only
	        // from the destructor. Once this defect is fixed, the following line must be uncommented: 
	        // SendNetworkStatusEvent(KNetworkInterfaceDown, RPacketService::EStatusUnattached);
			//**********************************************************************************************
			
			__FLOG_1(_L("Setting default QoS on primary context failed: mark lower NIF binder for context[%d] for async deletion."), iPrimaryContextId);
			// We are about to delete the primary context NIF: this will notify Nifman that SPUD is finished.
			DisposeOfBinder(ref);
			}
			
        return KErrNone;
		}

	case KContextCreate:
		{
        ASSERT(aOption.Ptr());
		TContextParameters& opt=*reinterpret_cast<TContextParameters*>(const_cast<TUint8*>(aOption.Ptr()));
		ASSERT(opt.iContextType == ESecondaryContext);

		TContextId id(KAllContexts);	// placeholder context ID
		CSpudBinderRef* ref = NULL;
		TRAPD(rc, ref = BindMan()->GetNewRefForSecondaryL(id););
		if(KErrNone != rc)
			{
			__FLOG_1(_L("Error %d creating a binder for the lower NIF"), rc);
			opt.iReasonCode = rc;  // The error code is the only argument we can pass to GUQoS, because there is no context.
    		return KErrNone;
    		}


		TRAP(rc, BindMan()->LoadNifL(iName, *ref);)
		if (rc != KErrNone)
			{
			__FLOG_1(_L("Error %d loading the lower NIF"), rc);
			FillInParameters(opt, id, rc);
			return KErrNone;
			}

		ASSERT(ref->IsBound());
        ref->SetState(ESpudStartingSecondary);


		// Reset the default QoS and TFT here. KContextQoSSet should arrive soon
		// with the proper values.
#ifdef SYMBIAN_NETWORKING_UMTSR5
			RPacketQoS::TQoSR5Requested qos;
#else
			RPacketQoS::TQoSR99_R4Requested qos;
#endif 
// SYMBIAN_NETWORKING_UMTSR5 
		rc = iPdpFsmInterface.Set(id, qos);

		TTFTInfo tft;
        rc = iPdpFsmInterface.Set(id, tft);
		
		// Pass in the new context ID to SpudFsm to use for the new context

		// Notify SpudFsm
		__FLOG_1(_L("Sending SpudFsm event ECreateSecondaryPDPContext context %d"), id);
		rc = iPdpFsmInterface.Input(id, ECreateSecondaryPDPContext);

		// Set up the synchronous response
		FillInParameters(opt, id, rc);
    	return KErrNone;
		}
		
	case KContextDelete:
		{
        ASSERT(aOption.Ptr());
        TContextParameters& opt = *reinterpret_cast<TContextParameters*>(const_cast<TUint8*>(aOption.Ptr()));
       	CSpudBinderRef* ref = NULL;
		TRAPD(rc, ref = BindMan()->GetRefL(opt.iContextInfo.iContextId));
		if (rc != KErrNone)
			{
			__FLOG_1(_L("Error: KContextDelete specifies context %d which does not exist"), opt.iContextInfo.iContextId);
			opt.iReasonCode = rc;  // The error code is the only argument we can pass to GUQoS, because there is no context.
    		return KErrNone;
			}
		
		ASSERT(ref->IsBound());
		__FLOG_3(_L("KContextDelete context[%d] in state %S(%d)"), opt.iContextInfo.iContextId, SpudStateToText(ref->State()), ref->State());

		switch (ref->State())
			{
		// NIF is up: need to stop the NIF first, then delete the binder.	
		case ESpudGettingNegQoS:
		case ESpudStartingSecondaryLowerNif: 
		// Assumption: GUQoS will never delete primary context.
		case ESpudUp:
		case ESpudFlowOff:
		case ESpudSuspended:
		case ESpudFlowOffAndSuspended:
	        ref->SetState(ESpudContextDelete);
	        
	        if(BindMan()->IsLastContext(opt.iContextInfo.iContextId)) // Are we about to shutdown after this?
	        	{
	           	SetTerminateError(KErrCancel); // KErrCancel is normally used to indicate "graceful" 
	        	}							   // user-initiated shutdown.
		   
	        // GUQoS ordered the shutdown: Network is still OK: initiate graceful shutdown of the lower NIF.
	        ref->NifLink()->Stop(KErrCancel, MNifIfNotify::EDisconnect);
	        break;
	    
	    case ESpudStartingSecondary:  // NIF not started: need to delete the context.
			ref->SetState(ESpudWaitLinkDown); 

			// Delete context via SpudFsm
			__FLOG_1(_L("Sending SpudFsm event EContextDelete context %d"), opt.iContextInfo.iContextId);
			rc = iPdpFsmInterface.Input(opt.iContextInfo.iContextId, EContextDelete);
			ASSERT(rc == KErrNone);
			break;

		case ESpudWaitLinkDown:
			// We're in the middle of deleting. Ignore this request.
			break;

		case ESpudLinkDown:
			// We're in the middle of deleting, but we no longer need to notify
			// GUQoS when done.
			ref->SetState(ESpudWaitLinkDown);
			break;
	
		case ESpudWaitBinderDelete: // context deleted, NIF about to be deleted: ignore.
			break;
			
		default:
			__FLOG_2(_L("Unexpected state %S(%d)"), SpudStateToText(ref->State()), ref->State());
	    	ASSERT(EFalse);
	    	break;
			}

		FillInParameters(opt, opt.iContextInfo.iContextId, rc);
        return KErrNone;
		}
		
	case KContextActivate:
		{
        ASSERT(aOption.Ptr());
		TContextParameters& opt=*reinterpret_cast<TContextParameters*>(const_cast<TUint8*>(aOption.Ptr()));
       	CSpudBinderRef* ref = NULL;
		// Validate context ID
		TRAPD(rc, ref = BindMan()->GetRefL(opt.iContextInfo.iContextId));
		if (KErrNone != rc)
			{
			opt.iReasonCode = rc; // Can supply error code only, as the context does not exist.
			__FLOG_1(_L("Error: KContextActivate specifies context %d which does not exist."), opt.iContextInfo.iContextId);
			return KErrNone;
			}
		
		if(!ref->IsBound() || ref->State() == ESpudWaitBinderDelete)
			{
			__FLOG_1(_L("Error: KContextActivate specifies context %d which is not bound to lower NIF."), opt.iContextInfo.iContextId);
			rc = KErrNotReady; // that's what the GuQoS docs say
			FillInParameters(opt, opt.iContextInfo.iContextId, rc);
			return KErrNone;
			}
			
		__FLOG_3(_L("KContextActivate context %d in state %S(%d)"), opt.iContextInfo.iContextId, SpudStateToText(ref->State()), ref->State());

        ASSERT(ref->State() == ESpudStartingSecondary || ref->State() == ESpudUp);


		// Notify SpudFsm
		__FLOG_1(_L("Sending SpudFsm event EContextActivate context %d"), opt.iContextInfo.iContextId);
		rc = iPdpFsmInterface.Input(opt.iContextInfo.iContextId, EContextActivate);
		// Set up the synchronous response
		FillInParameters(opt, opt.iContextInfo.iContextId, rc);
        return KErrNone;
		}

	case KContextQoSSet:
		{
		TContextParameters& opt=*reinterpret_cast<TContextParameters*>(const_cast<TUint8*>(aOption.Ptr()));
       	CSpudBinderRef* ref = NULL;
		// Validate context ID
		TRAPD(rc, ref = BindMan()->GetRefL(opt.iContextInfo.iContextId));
		if (KErrNone != rc)
			{
			__FLOG_1(_L("Error: KContextQoSSet specifies context %d which does not exist"), opt.iContextInfo.iContextId);
			opt.iReasonCode = rc; // Can only supply error code, as the context does not exist.
			return KErrNone;
			}		
		
		if(!ref->IsBound() || ref->State() == ESpudWaitBinderDelete)
			{
			__FLOG_1(_L("Error: KContextQoSSet specifies context %d which is not bound to the lower NIF."), opt.iContextInfo.iContextId);
			rc = KErrNotReady; // that's what the GuQoS docs say
			FillInParameters(opt, opt.iContextInfo.iContextId, rc);
			return KErrNone;
			}
		__FLOG_3(_L("KContextQoSSet context %d in state %S(%d)"), opt.iContextInfo.iContextId, SpudStateToText(ref->State()), ref->State());

		ASSERT(ref->State() == ESpudStartingSecondary
            || ref->State() == ESpudUp
            || ref->State() == ESpudFlowOff
            || ref->State() == ESpudSuspended
            || ref->State() == ESpudFlowOffAndSuspended
            || ref->State() == ESpudLinkDown );
		
#ifdef SYMBIAN_NETWORKING_UMTSR5
        RPacketQoS::TQoSR5Requested qos;
#else
    RPacketQoS::TQoSR99_R4Requested qos;
#endif 
// SYMBIAN_NETWORKING_UMTSR5 

		// Store QoS parameters
        opt.iContextConfig.GetUMTSQoSReq(qos); 
		rc = iPdpFsmInterface.Set(opt.iContextInfo.iContextId, qos);

		// Notify SpudFsm
		if (rc == KErrNone)
			{
			__FLOG_1(_L("Sending SpudFsm event EContextQoSSet context %d"), opt.iContextInfo.iContextId);
			rc = iPdpFsmInterface.Input(opt.iContextInfo.iContextId, EContextQoSSet);
			}
		FillInParameters(opt, opt.iContextInfo.iContextId, rc);
        return KErrNone;
		}

	case KContextTFTModify:
		{
		TContextParameters& opt=*reinterpret_cast<TContextParameters*>(const_cast<TUint8*>(aOption.Ptr()));
       	CSpudBinderRef* ref = NULL;
		// Validate context ID
		TRAPD(rc, ref = BindMan()->GetRefL(opt.iContextInfo.iContextId));
		if (KErrNone != rc)
			{
			__FLOG_1(_L("Error: KContextTFTModify specifies context %d which does not exist"), opt.iContextInfo.iContextId);
			opt.iReasonCode = rc; // Can only supply error code as the context does not exist.
			return KErrNone;
			}

		// keep local reference to Primary Context up to date
		if (opt.iContextType == EPrimaryContext) 
			{
			iPrimaryContextId = opt.iContextInfo.iContextId;
			}
			
		if(!ref->IsBound() || ref->State() == ESpudWaitBinderDelete)
			{
			__FLOG_1(_L("Error: KContextTFTModify specifies context %d which is not bound to the lower NIF."), opt.iContextInfo.iContextId);
			rc = KErrNotReady; // that's what the GuQoS docs say
			FillInParameters(opt, opt.iContextInfo.iContextId, rc);
			return KErrNone;
			}
		__FLOG_3(_L("KContextTFTModify context %d in state %S(%d)"), opt.iContextInfo.iContextId, SpudStateToText(ref->State()), ref->State());

		ASSERT(ref->State() == ESpudStartingSecondary || 
			   ref->State() == ESpudUp ||
		       ref->State() == ESpudWaitLinkDown  ||
		       ref->State() == ESpudLinkDown ||
		       ref->State() == ESpudGettingNegQoS);
		// It is assumed that the TFT can be modified before the ctx reported activation 

		// Store TFT parameters
		TTFTInfo tft;
        opt.iContextConfig.GetTFTInfo(tft); 

        rc = iPdpFsmInterface.Set(opt.iContextInfo.iContextId, tft);
		if (rc != KErrNone)
			{
			__FLOG_1(_L("Error %d setting TFT"), rc);
			FillInParameters(opt, opt.iContextInfo.iContextId, rc);
			return KErrNone;
			}
        rc = iPdpFsmInterface.Set(opt.iContextInfo.iContextId, opt.iTFTOperationCode);
		if (rc != KErrNone)
			{
			__FLOG_1(_L("Error %d setting TFT operation code"), rc);
			FillInParameters(opt, opt.iContextInfo.iContextId, rc);
			return KErrNone;
			}

		// Notify SpudFsm
		__FLOG_1(_L("Sending SpudFsm event EContextTFTModify context %d"), opt.iContextInfo.iContextId);
		rc = iPdpFsmInterface.Input(opt.iContextInfo.iContextId, EContextTFTModify);

		FillInParameters(opt, opt.iContextInfo.iContextId, rc);
        return KErrNone;
		}

	case KContextModifyActive:
		{
		TContextParameters& opt=*reinterpret_cast<TContextParameters*>(const_cast<TUint8*>(aOption.Ptr()));
       	CSpudBinderRef* ref = NULL;
		// Validate context ID
		TRAPD(rc, ref = BindMan()->GetRefL(opt.iContextInfo.iContextId));
		if (KErrNone != rc)
			{
			__FLOG_1(_L("Error: KContextModifyActive specifies context %d which does not exist"), opt.iContextInfo.iContextId);
			opt.iReasonCode = rc;
			return KErrNone;
			}
			
		if(!ref->IsBound() || ref->State() == ESpudWaitBinderDelete)
			{
			__FLOG_1(_L("Error: KContextModifyActive specifies context %d which is not bound to the lower NIF."), opt.iContextInfo.iContextId);
			rc = KErrNotReady; // that's what the GuQoS docs say
			FillInParameters(opt, opt.iContextInfo.iContextId, rc);
			return KErrNone;
			}

		// Notify SpudFsm
		__FLOG_1(_L("Sending SpudFsm event EContextModifyActive context %d"), opt.iContextInfo.iContextId);
		rc = iPdpFsmInterface.Input(opt.iContextInfo.iContextId, EContextModifyActive);

		FillInParameters(opt, opt.iContextInfo.iContextId, rc);
        return KErrNone;
		}
		
	case KInitialisePdpFsm:
      {
      MPdpFsmInterface* pdpFsm = *reinterpret_cast<MPdpFsmInterface**>(const_cast<TUint8*>(aOption.Ptr()));
      iPdpFsmInterface.Init(pdpFsm);
      return KErrNone;
      }

	default:
		__FLOG_1(_L("Unhandled event %d"), aName);
		break;
		}
	return KErrNotSupported;
	}

/**
Receives link up indication from a lower NIF.

@param aContextId PDP context ID of the associated lower NIF
*/
void CSpudMan::LinkLayerUp(TContextId aContextId)
	{
	CSpudBinderRef* ref = NULL;
	TRAPD(rc, ref = iBindMan->GetRefL(aContextId));
	__ASSERT_ALWAYS(rc == KErrNone, Panic());
	ASSERT(ref->IsBound() && ref->State() != ESpudWaitBinderDelete);
	
	__FLOG_3(_L("Lower NIF LinkLayerUp on context[%d], in state %S(%d)"), aContextId, SpudStateToText(ref->State()), ref->State());
	switch (ref->State())
		{
	case ESpudStartingPrimaryLowerNif:
		{
	    ref->SetState(ESpudUp);
		if (AreQoSEventsEnabled())
			{
			SendPrimaryContextCreated(aContextId, KErrNone);
			}
		// If QoS is not yet enabled, SendPrimaryContextCreated() will be called when it is

		Notify()->LinkLayerUp();

		// If mobile IP is enabled, this progress notification should not be sent here.
		// This will need to be addressed if MIP is ever enabled for UMTS.
		Notify()->IfProgress(KLinkLayerOpen, KErrNone);
		}
		break;

	case ESpudStartingSecondaryLowerNif:
		{
		// lower nif is up, now get the Negotiated QoS
	    ref->SetState(ESpudGettingNegQoS);
		
		TInt rc = iPdpFsmInterface.Input(aContextId, EGetNegQoS);
		ASSERT(rc == KErrNone);
		// Now wait for retrieval of negotiated QoS.
		// The following 2 functions will be fired from CSpudMan::HandleGetNegQoSEvent():
		//    FillInContextConfig(iTempContextConfig, aContextId);
		//    SendContextActivateEvent(aContextId, iTempContextConfig, KErrNone);
		}
		break;
		
	default:
		__FLOG_3(_L("Lower NIF LinkLayerUp on context[%d], in unexpected state %S(%d)"), aContextId, SpudStateToText(ref->State()), ref->State());
		ASSERT(EFalse);
		}
	}

/**
Receives link down indication from a lower NIF.

@param aContextId Valid PDP context ID of the associated lower NIF
@param aReason A Symbian OS error code indicating the reason for the link closing down
@param aAction The action that should be taken as a result of link layer down being signalled
*/
void CSpudMan::LinkLayerDown(TContextId aContextId, TInt aReason, MNifIfNotify::TAction aAction)
	{
	__FLOG_3(_L("CSpudMan::LinkLayerDown: context %d reason %d action %d"), aContextId, aReason, aAction);

	if (aAction == MNifIfNotify::ENoAction)
		{
		// This call indicates a renegotiation of the lower link, not that the link has
		// actually gone down. This is of no interest to SPUD.
		__FLOG_0(_L("Ignoring MNifIfNotify::ENoAction"));
		return;
		}

	CSpudBinderRef* ref = NULL;
	TRAPD(rc, ref = iBindMan->GetRefL(aContextId));
	__ASSERT_ALWAYS(rc == KErrNone, Panic());
	__FLOG_3(_L("context %d is in state %S(%d)"), aContextId, SpudStateToText(ref->State()), ref->State());

	
	TBool isUpperFlowOn = EFalse; // Can GUQoS attempt to send on this PDP context?
	
	switch (ref->State())
		{
	case ESpudUp:
		isUpperFlowOn = ETrue; // ESpudUp is the only state where GUQoS is allowed to send on us.
		// Fall through
	case ESpudFlowOff:// NIF & context were activated succcessfully.
	case ESpudSuspended:
	case ESpudFlowOffAndSuspended:
		ref->SetState(ESpudLinkDown); // Must Notify GUQoS that context is down.
		
		// Must inform upper layers they can no longer send on this context.
		if (isUpperFlowOn && AreQoSEventsEnabled()) 
			{
			// At this point the context status does not indicate to GUQoS that the context is about to go down.
			// Most likely, the status is EStatusActive, indicating that the context can handle requests.
			// Since the lower NIF is down, we are about to delete the context, so logically, this context
			// is EStatusDeactivating.(From spudman's PoV, a context is a union of lower NIF and ETel's RPacketContext)
			// GUQoS does not know this, it may try to issue requests,  
			// such as send packets on this context, modify TFT, modify QoS, etc.
			// Note: this does not seem to happen, but it is feasible.
			
			// N.B. Ideally, we should signal context change status in not just in EStateUp, but in 
			// EStateFlowOff etc, i.e. any state in which the context is alive. Unfortunately, GUQoS 
			// does not allow us to do this - there is no pure "ContextStatusChange" upcall on GUQoS.
			// Context Parameters Change upcall should not be used, because GUQoS interprets it as a
			// QoS parameters change, and sends QoSEventAdapt to the QoS framework. This is wrong.
			//
			// We could send ContextBlocked in FlowOff / Suspended states, however, this is problematic,
			// because GUQoS has already received one of these notifications. We could "trick" it and cycle
			// the state (send Flow On / Resume, then Flow Off / Suspend again), but this hackery should be
			// avoided unless absolutely necessary. So far, GUQoS does not seem to do anything beside sending
			// a packet on a deactivaing context, so we only worry about it.
			
			// N.B. SpudFSM is asynchronous, it will not issue a an actual deletion request on ETel until this RunL
			// returns. This means that the context status will not be updated to EStatusDeactiving in ETel 
			// until then, so when we fill in context paramters, the status is going to be EStatusActive.
			// We "sneak in" the correct status by overriding it. Doing it in SpudFSM would be wrong, because
			// SpudFSM reflects the ETel side of the context, so overriding the status there is misleading,
			// because it can be used internally by SpudFSM / SpudTel.
			
			__FLOG_0(_L("Upper flow on the context is On: flow Off GUQoS to prevent it from sending on a context with dead lower NIF."));
			iContextStatusOverride = RPacketContext::EStatusDeactivating; 
			SendContextBlockedEvent(aContextId); // resets the override
			isUpperFlowOn = EFalse; 
			}
		
		// The only way we can notify GUQoS once the context has been activated is
		// to delete it via the SpudFsm
		__FLOG_1(_L("Sending SpudFsm event EContextDelete context %d"), aContextId);
		rc = iPdpFsmInterface.Input(aContextId, EContextDelete);
		__FLOG_1(_L("SpudFsm::Input() returned status %d"), rc);
		if (rc == KErrNotReady) //This is becuase the FSM has been shut down via UMTSGPRSSCPR
			{
			HandleContextDeleteEvent(aContextId, aReason); //Just pretend that the fsm returned something sensible
			}
		ASSERT(rc == KErrNone || rc == KErrNotReady);
		break;

	case ESpudGettingNegQoS: // CNifIfLink::Stop will handle this just as if the NIF was still being CNifIfLink::Start'ed.
	case ESpudStartingSecondaryLowerNif: // Attempt to bring up the lower NIF was made.
		ref->SetState(ESpudStartingSecondary); // GUQoS should delete us later. 
		// Notify GUQoS that Activation failed. It will take the control from here, most likely deleting the context.
		FillInContextConfig(iTempContextConfig, aContextId);
		if(ref->Error() != KErrNone)
			{
			SendContextActivateEvent(aContextId, iTempContextConfig, ref->Error());
			}
		else
			{
			SendContextActivateEvent(aContextId, iTempContextConfig, aReason);
			}
		break;
	
	case ESpudContextDelete:
		ref->SetState(ESpudWaitLinkDown); // GUQoS triggered us: don't need to notify it.

		// Delete context via SpudFsm
		__FLOG_1(_L("Sending SpudFsm event EContextDelete context %d"), aContextId);
		rc = iPdpFsmInterface.Input(aContextId, EContextDelete);
		__FLOG_1(_L("SpudFsm::Input() returned status %d"), rc);
		ASSERT(rc == KErrNone);
		break;
		
	case ESpudStartingSecondary:
	case ESpudLinkDown: // Added for INC066156.
	case ESpudWaitLinkDown:
		// Link has finally gone down. Context is already deleted.
    	__FLOG_1(_L("Lower NIF reported LinkLayerDown: mark the binder for context[%d] for async deletion"), aContextId);
		DisposeOfBinder(ref);
		break;
	
	case ESpudStartingPrimaryLowerNif:
		{
		SetTerminateError(aContextId, aReason); // SPUD is going to terminate.
		SetTerminateError(KErrCouldNotConnect);
		
		// The primary context is managed by SPUD. We delete it ourselves.
		__FLOG_1(_L("Sending SpudFsm event EContextDelete context %d"), aContextId);
		ref->SetState(ESpudWaitLinkDown);
		rc = iPdpFsmInterface.Input(aContextId, EContextDelete);
		__FLOG_1(_L("SpudFsm::Input() returned status %d"), rc);
		ASSERT(rc == KErrNone);
		
		// Meanwhile, notify GUQoS about the failed primary creation
		if (AreQoSEventsEnabled())
			{
			SendPrimaryContextCreated(aContextId, aReason);
			}
		break;
		}	
			
	default:
		__FLOG_2(_L("Unexpected state %S(%d)"), SpudStateToText(ref->State()), ref->State());
		ASSERT(EFalse);
		break;
		}
	}

/**
Receives flow off indication from a lower NIF.

@param aContextId Valid PDP context ID of the associated lower NIF
*/
void CSpudMan::StopSending(TContextId aContextId)
	{
	__FLOG_1(_L("CSpudMan::StopSending context %d"), aContextId);
	CSpudBinderRef* ref = NULL;
	TRAPD(rc, ref = iBindMan->GetRefL(aContextId));
	__ASSERT_ALWAYS(rc == KErrNone, Panic());
	__FLOG_3(_L("context %d is in state %S(%d)"), aContextId, SpudStateToText(ref->State()), ref->State());
	switch (ref->State())
		{
	case ESpudUp:
		ref->SetState(ESpudFlowOff);
		if (AreQoSEventsEnabled())
			{
			SendContextBlockedEvent(aContextId);
			}
		break;

	case ESpudSuspended:
		ref->SetState(ESpudFlowOffAndSuspended);
		break;

	case ESpudContextDelete:
	case ESpudWaitLinkDown:
	case ESpudLinkDown: //sometimes this leaks in
		// ignore
		break;	
	
	default:
		// error
		__FLOG_2(_L("Unexpected state %S(%d)"), SpudStateToText(ref->State()), ref->State());
		ASSERT(EFalse);
		break;
		}
	}

/**
Receives flow on indication from a lower NIF.

@param aContextId Valid PDP context ID of the associated lower NIF
*/
void CSpudMan::StartSending(TContextId aContextId)
	{
	__FLOG_1(_L("CSpudMan::StartSending context %d"), aContextId);
	CSpudBinderRef* ref = NULL;
	TRAPD(rc, ref = iBindMan->GetRefL(aContextId));
	__ASSERT_ALWAYS(rc == KErrNone, Panic());
	__FLOG_3(_L("context %d is in state %S(%d)"), aContextId, SpudStateToText(ref->State()), ref->State());
	switch (ref->State())
		{
	case ESpudGettingNegQoS: // Lower NIF for the 2ndary ctx is up, waiting for negotiated QoS.
		break; // GUQoS, TCP/IP stack will be notified when negotiated QoS is retrieved. 
		// Notes: 
		// 1.there is a *potential* race condition where the negotiated QoS is retrived before lower NIF signals StartSending.
		// This does not seem to happen with PPP. If this happens somehow, the ctx will be in the "UP" state and it will be handled correctly.
		// 2. Theoretically, the lower NIF may report LinkLayerUp (triggering retrieval of negotiated QoS), then signal 
		// StartSending much later (e.g.  a PPP implementation singalling LinkLayerUp from LCP, but StartSending from NCP).
		// SPUD CANNOT HANDLE THIS.
		// Existing Raw IP and PPP NIFs signal StartSending immediately after LinkLayerUp, so this has no consequences.

	case ESpudUp:
		// This can happen for the initial StartSending after the lower NIF comes up
		// Treat it the same as ESpudFlowOff and fall through.
	case ESpudFlowOff:
		ref->SetState(ESpudUp);

		// This must only be done AFTER the KPrimaryContextCreated event is sent (which in this state it is)
		// It's not clear if StartSending is needed/allowed in addition to the GUQoS event.
	    BindMan()->SpudProtocol()->DoStartSending();

		if (AreQoSEventsEnabled())
			{
			SendContextUnblockedEvent(aContextId);
			}
		break;

	case ESpudFlowOffAndSuspended:
		ref->SetState(ESpudSuspended);
		break;

	case ESpudSuspended:
	case ESpudContextDelete:
	case ESpudWaitLinkDown:
		// ignore
		__FLOG_1(_L("Ignored StartSending on context %d (this should be OK)"), aContextId);
		break;	
	
	default:
		// We have encountered a serious problem. If we get StartSending before reaching the ESpudUp
		// state, we'll lose it and the upper networking protocol will never be notified.
		// As long as the lower NIF calls LinkLayerUp before StartSending, we'll be fine.
		__FLOG_1(_L("Can't send unblocked event now on context %d; this may cause problems"), aContextId);
		ASSERT(EFalse);
		break;
		}
	}


//*****************************************************************************
// Event senders to GUQoS
//*****************************************************************************

/**
Sends event to GUQoS.

@param aName event identifier
@param aOption TPckg<> event data
*/
void CSpudMan::RaiseEvent(TUint aName, TDes8& aOption) const
	{
	__FLOG_2(_L("Sending event %S(%d) to GUQoS"), SpudFsmEventToText(aName), aName);
	iQosEventHandler->Event(reinterpret_cast<CProtocolBase*>(iBindMan->SpudMux()), aName, aOption); 
	}

/**
Fills in common event parameters for the given context.

@param aParams parameter structure
@param aContextId Valid PDP context ID
*/
void CSpudMan::FillInParameters(TContextParameters& aParams, TContextId aContextId, TInt aError) const
	{
   	CSpudBinderRef* ref = NULL;
	TRAPD(rc, ref = BindMan()->GetRefL(aContextId));
	ASSERT(rc == KErrNone);
	
	TContextType type(EContextTypeUnknown);
	if (rc == KErrNone)
		{
		switch (ref->State())
			{
		case ESpudHaveQos:
		case ESpudCreatingPrimary:
		case ESpudStartingPrimary:
			type = EPrimaryContext;
			break;
			
		case ESpudStartingSecondary:
			type = ESecondaryContext;
			break;
		
		default:
			type = (aContextId == iPrimaryContextId) ? EPrimaryContext : ESecondaryContext;;
			break;
			}
		}
    aParams.iContextType = type; // Context type
    aParams.iReasonCode = aError;  // Error code
    //aParams.iContextInfo.iStatus = StateToStatus(*ref);
    iPdpFsmInterface.Get(aContextId, aParams.iContextInfo.iStatus);
    aParams.iContextInfo.iContextId = aContextId;
	}

/**
Fill in context configuration parameter structure using SpudFsm's parameters.

@param aConfig parameter structure
@param aContextId PDP context ID
*/
void CSpudMan::FillInContextConfig(TContextConfig& aConfig, TContextId aContextId) const
	{
#ifdef SYMBIAN_NETWORKING_UMTSR5
		RPacketQoS::TQoSR5Negotiated qos;
#else
		RPacketQoS::TQoSR99_R4Negotiated qos;
#endif 
// SYMBIAN_NETWORKING_UMTSR5 

	iPdpFsmInterface.Get(aContextId, qos);
	aConfig.SetUMTSQoSNeg(qos);

	iPdpFsmInterface.Get(aContextId, iTempTftInfo);
	aConfig.SetTFTInfo(iTempTftInfo);

	iPdpFsmInterface.Get(aContextId, iTempGprsContext);
	aConfig.SetContextConfig(iTempGprsContext);
	}

/**
Sends KPrimaryContextCreated event to GUQoS.
*/
void CSpudMan::SendPrimaryContextCreated(TContextId aContextId, TInt aError)
	{
	__FLOG_2(_L("SendPrimaryContextCreated context %d error %d"), aContextId, aError);

	if (!iQosEventsEnabled)
		{
		__FLOG_0(_L("Error: sending events is disabled - GuQoS not present ?"));
		return;
		}
	// We make this assumption in various places
	ASSERT(aContextId == 0);

	TContextParameters primaryContextCreatedEvent;
	FillInParameters(primaryContextCreatedEvent, aContextId, aError);
	TPckg<TContextParameters> event(primaryContextCreatedEvent);
	RaiseEvent(KPrimaryContextCreated, event);
	}


/**
Sends KSecondaryContextCreated event to GUQoS.

@param aContextId Context ID
@param aError error code
*/
void CSpudMan::SendSecondaryContextCreated(TContextId aContextId, TInt aError)
	{
	__FLOG_2(_L("SendSecondaryContextCreated context %d error %d"), aContextId, aError);

	if (!iQosEventsEnabled)
		{
		__FLOG_0(_L("Error: sending events is disabled - GuQoS not present ?"));
		return;
		}

	// We make this assumption in various places
	ASSERT(aContextId != 0);
	
	TContextParameters event;
	FillInParameters(event, aContextId, aError);
	TPckg<TContextParameters> eventPckg(event);
	RaiseEvent(KSecondaryContextCreated, eventPckg);
	}


/**
Sends KContextBlockedEvent event to GUQoS.
*/
void CSpudMan::SendContextBlockedEvent(TContextId aContextId)
	{
	__FLOG_2(_L("SendContextBlockedEvent context %d error %d"), aContextId, KErrNone);
	
	if (!iQosEventsEnabled)
		{
		__FLOG_0(_L("Error: sending events is disabled - GuQoS not present ?"));
		return;
		}

	TContextParameters event;
	FillInParameters(event, aContextId);
	
	/** The status we want to signal to GUQoS may be different from the context status we get from SpudFSM
	E.g., if we are about to deactivate the context, the logical status of the context is EStatusDeactivating, rather than
	EStatusActive, even though that's what SpudFSM will tell us */
	if(RPacketContext::EStatusUnknown != iContextStatusOverride)
		{
		__FLOG_2(_L("SendContextBlockedEvent: context status overriden to %d, original: %d."), 
			iContextStatusOverride, event.iContextInfo.iStatus);
			
		event.iContextInfo.iStatus = iContextStatusOverride;			
		iContextStatusOverride = RPacketContext::EStatusUnknown; 
		}
	
	TPckg<TContextParameters> eventPckg(event);
	RaiseEvent(KContextBlockedEvent, eventPckg);
	}

/**
Sends KContextUnblockedEvent event to GUQoS.
*/
void CSpudMan::SendContextUnblockedEvent(TContextId aContextId)
	{
	__FLOG_2(_L("SendContextUnblockedEvent context %d error %d"), aContextId, KErrNone);

	if (!iQosEventsEnabled)
		{
		__FLOG_0(_L("Error: sending events is disabled - GuQoS not present ?"));
		return;
		}
	
	TContextParameters event;
	FillInParameters(event, aContextId);
	TPckg<TContextParameters> eventPckg(event);
	RaiseEvent(KContextUnblockedEvent, eventPckg);
	}

/**
Sends KContextQoSSetEvent event to GUQoS.

@param aContextId Context ID
@param aError error code
*/
void CSpudMan::SendContextQoSSetEvent(TContextId aContextId, TInt aError)
	{
	__FLOG_2(_L("SendContextQoSSetEvent context %d error %d"), aContextId, aError);

	if (!iQosEventsEnabled)
		{
		__FLOG_0(_L("Error: sending events is disabled - GuQoS not present ?"));
		return;
		}
	
	TContextParameters event;
	FillInParameters(event, aContextId, aError);
	TPckg<TContextParameters> eventPckg(event);
	RaiseEvent(KContextQoSSetEvent, eventPckg);
	}

/**
Sends KContextTFTModifiedEvent event to GUQoS.

@param aContextId Context ID
#param aTFTOperationCode TFT operation code
@param aError error code
*/
void CSpudMan::SendContextTFTModifiedEvent(TContextId aContextId, TTFTOperationCode aTFTOperationCode, TInt aError)
	{
	__FLOG_2(_L("SendContextTFTModifiedEvent context %d error %d"), aContextId, aError);

	if (!iQosEventsEnabled)
		{
		__FLOG_0(_L("Error: sending events is disabled - GuQoS not present ?"));
		return;
		}
	
	TContextParameters event;
	FillInParameters(event, aContextId, aError);
	// Also need to fill in TTFTOperationCode, an undocumented requirement
	event.iTFTOperationCode = aTFTOperationCode;
	TPckg<TContextParameters> eventPckg(event);
	RaiseEvent(KContextTFTModifiedEvent, eventPckg);
	}

/**
Sends KContextModifyActiveEvent event to GUQoS.

@param aContextId Context ID
@param aContextConfig Configuration parameters for this context
@param aError error code
*/
void CSpudMan::SendContextModifyActiveEvent(TContextId aContextId, TContextConfig& aContextConfig, TInt aError)
	{
	__FLOG_2(_L("SendContextModifyActiveEvent context %d error %d"), aContextId, aError);

	if (!iQosEventsEnabled)
		{
		__FLOG_0(_L("Error: sending events is disabled - GuQoS not present ?"));
		return;
		}
	
	TContextParameters event;
	FillInParameters(event, aContextId, aError);
	event.iContextConfig = aContextConfig;
	TPckg<TContextParameters> eventPckg(event);
	RaiseEvent(KContextModifyActiveEvent, eventPckg);
	}

/**
Sends KContextActivateEvent event to GUQoS.

@param aContextId Context ID
@param aContextConfig Configuration parameters for this context
@param aError error code
*/
void CSpudMan::SendContextActivateEvent(TContextId aContextId, TContextConfig& aContextConfig, TInt aError)
	{
	__FLOG_2(_L("SendContextActivateEvent context %d error %d"), aContextId, aError);

	if (!iQosEventsEnabled)
		{
		__FLOG_0(_L("Error: sending events is disabled - GuQoS not present ?"));
		return;
		}
	
	TContextParameters event;
	FillInParameters(event, aContextId, aError);
	event.iContextConfig = aContextConfig;
	TPckg<TContextParameters> eventPckg(event);
	RaiseEvent(KContextActivateEvent, eventPckg);
	}

/**
Sends KContextParametersChangeEvent event to GUQoS.

@param aContextId Context ID
@param aContextConfig Configuration parameters for this context
@param aError error code
*/
void CSpudMan::SendContextParametersChangeEvent(TContextId aContextId, TContextConfig& aContextConfig, TInt aError)
	{
	__FLOG_2(_L("SendContextParametersChangeEvent context %d error %d"), aContextId, aError);

	if (!iQosEventsEnabled)
		{
		__FLOG_0(_L("Error: sending events is disabled - GuQoS not present ?"));
		return;
		}
	
	TContextParameters event;
	FillInParameters(event, aContextId, aError);
	event.iContextConfig = aContextConfig;
	TPckg<TContextParameters> eventPckg(event);
	RaiseEvent(KContextParametersChangeEvent, eventPckg);
	}

/**
Sends KContextDeleteEvent event to GUQoS.

@param aContextId Context ID
*/
void CSpudMan::SendContextDeleteEvent(TContextId aContextId)
	{
	__FLOG_2(_L("SendContextDeleteEvent context %d error %d"), aContextId, KErrNone);

	if (!iQosEventsEnabled)
		{
		__FLOG_0(_L("Error: sending events is disabled - GuQoS not present ?"));
		return;
		}
	
	TContextParameters event;
	FillInParameters(event, aContextId);
	TPckg<TContextParameters> eventPckg(event);
	RaiseEvent(KContextDeleteEvent, eventPckg);
	}

/**
Sends KNetworkStatusEvent event to GUQoS.

@param aEventCode Event code
@param aStatus Network status
*/
void CSpudMan::SendNetworkStatusEvent(TNetworkEventCode aEventCode, RPacketService::TStatus aStatus)
	{
	__FLOG_2(_L("SendNetworkStatusEvent event code %d status %d"), aEventCode, aStatus);

	if (!iQosEventsEnabled) // QoS events not turned on yet, or have been turned off by GUQoS
		{
		__FLOG_0(_L("Error: sending events is disabled - GuQoS not present ?"));
		return;
		}
	
	TNetworkParameters event;
	event.iNetworkEventCode = aEventCode;
	event.iNetworkStatus = aStatus;
	TPckg<TNetworkParameters> eventPckg(event);
	RaiseEvent(KNetworkStatusEvent, eventPckg);
	}


//*****************************************************************************
// CNifIfLink methods
//*****************************************************************************


/**
Return the link protocol handler object.

@param aName Protocol name desired
@return Pointer to link protocol handler (ownership is transferred)
*/
CNifIfBase* CSpudMan::GetBinderL(const TDesC& aName)
	{
    __FLOG_1(_L("CSpudMan::GetBinderL %S"), &aName);
    iName = aName;
	return static_cast<CNifIfBase*>(iBindMan->TransferSpudMux());
	}

/**
Return information about the SPUD NIF.

@param aInfo Receives the NIF interface info
*/
void CSpudMan::Info(TNifIfInfo& aInfo) const
	{
	CSpudBinderRef* ref = NULL;
	// Get the binder for the first (default) lower NIF.
	TRAPD(err, ref = iBindMan->GetAnyRefL());
	if (err == KErrNone)
		{
		// Read the protocol supported value from the lower NIF
		ref->NifLink()->Info(aInfo);
		ASSERT(aInfo.iFlags == (KNifIfIsBase | KNifIfUsesNotify | KNifIfIsLink | KNifIfCreatedByFactory | KNifIfCreatesBinder));
		}
	else
		{
		aInfo.iProtocolSupported=KProtocolUnknown;
		}
	
	aInfo.iVersion = TVersion(KSpudMajorVersionNumber, KSpudMinorVersionNumber, KSpudBuildVersionNumber);
	aInfo.iFlags = KNifIfIsBase | 
                   KNifIfUsesNotify | 
                   KNifIfIsLink | 
                   KNifIfCreatedByFactory | 
                   KNifIfCreatesBinder;
	aInfo.iName = KSpudName;
	}

/**
Processes notifications from Agent

@param aEvent Event type
@param aInfo Data relating to event

@return Error code
*/
TInt CSpudMan::Notification(TAgentToNifEventType aEvent, void * aInfo)
	{
	__FLOG_1(_L("CSpudMan::Notification event %d"), aEvent);
	TInt rc = KErrNotSupported;
	switch (aEvent)
		{
	case EAgentToNifEventTypeModifyInitialTimer:
	case EAgentToNifEventTypeDisableTimers:
	case EAgentToNifEventTypeEnableTimers:
	case EAgentToNifEventTsyConfig:
	case EAgentToNifEventTsyConnectionSpeed:
		// Send notification to all lower NIFs
		rc = KErrNotReady;
		TContextId i;
		for (i=0; i < KMaxPdpContexts; ++i)
			{
			CSpudBinderRef* ref = NULL;
			TRAP(rc, ref = iBindMan->GetRefL(i));
			if (rc == KErrNone)
				{
				rc = ref->NifLink()->Notification(aEvent, aInfo);
				}
			}
		break;

	case EAgentToNifEventTypeGetDataTransfer:
		{
		TPckg<RPacketContext::TDataVolume>* totalDataPackage = (TPckg<RPacketContext::TDataVolume>*) aInfo;
		RPacketContext::TDataVolume& totalData = (*totalDataPackage)();
		totalData.iBytesSent = 0;
		totalData.iOverflowCounterSent = 0;
		totalData.iBytesReceived = 0;
		totalData.iOverflowCounterReceived = 0;

		RPacketContext::TDataVolume data;
		TPckg<RPacketContext::TDataVolume> dataPackage(data);

		// Add up data reported by all NIFs
		rc = KErrNotReady;
		TContextId i;
		for (i=0; i < KMaxPdpContexts; ++i)
			{
			CSpudBinderRef* ref = NULL;
			TRAP(rc, ref = iBindMan->GetRefL(i));
			if (rc == KErrNone)
				{
				rc = ref->NifLink()->Notification(aEvent, &dataPackage);
				if (rc == KErrNone)
					{
					totalData.iBytesSent += data.iBytesSent;
					totalData.iOverflowCounterSent += data.iOverflowCounterSent;
					totalData.iBytesReceived += data.iBytesReceived;
					totalData.iOverflowCounterReceived += data.iOverflowCounterReceived;
					}
				}
			}
		break;
		}

	case EAgentToNifEventTypeDisableConnection:
		// TODO: what to do with this?
	default:
		__FLOG_1(_L("Notification event %d was ignored"), aEvent);
		break;
		}

	return rc;
	}


/**
Start the link.
At this point only the primary PDP context is valid.

@return Error code
*/
TInt CSpudMan::Start()
	{
	__FLOG_1(_L("CSpudMan::Start(0x%x)"), this);

	// SpudTel needs TSY name from CommDb
	TName tsyName;
	ReadTsyName(tsyName);
	
	// Initialise SpudFsm
	TRAPD(err, InitPdpFsmInterfaceL());
	if (err != KErrNone)
	  {
	  __FLOG_1(_L("CSpudMan::Start: Failed to initialise the PDP Fsm Interface,Error = %d"),err);
	  return err;
     }

	// Open SpudFsm
	TRAP(err, iPdpFsmInterface.OpenL(this, tsyName));
	if (err != KErrNone)
	  {
	  __FLOG_1(_L("CSpudMan::Start: Failed to open the PDP Fsm Interface,Error = %d"),err);
	  return err;
     }


	// re-initialise the temporary data structure before retrieving 
	// GPRS config parameters from CommDB
   __FLOG_0(_L("CSpudMan::Start: Getting default GPRS settings from Commdb"));
	RetrieveGprsConfig(iTempGprsContext);
	
	TRAP(err, SetupSipServerAddrRetrievalL(iTempGprsContext.iProtocolConfigOption););
	
#ifdef SYMBIAN_NETWORKING_UMTSR5
	// Add the IMCN Signalling Status flag. IM CN status flag is retrieved from the Database
	// Request For the status of IM CN dedicated signalling context
	TRAP(err,SetIMCNSignallingFlagPcoL(iTempGprsContext.iProtocolConfigOption));
	
	// Not sure what can be done after trapping the error, because its not an error condition for starting of 
	// Primary PDP context.
#ifdef __FLOG_ACTIVE
	if(err != KErrNone)
		{
		__FLOG_1(_L("CSpudMan::Start: Failed to set IM CN signalling Flag.Error = %d"),err);
		}
#endif
#endif // SYMBIAN_NETWORKING_UMTSR5
	
	iPdpFsmInterface.Set(iPrimaryContextId, iTempGprsContext);
	if (err != KErrNone)
        {
        __FLOG_1(_L("CSpudMan::Start: Setup sip server address retrieval. Failed with %d"),err);
        return err;
        }
        
	if (iParkedDefaultQoS != NULL) 
        {
        __FLOG_0(_L("CSpudMan::Start: Found parked QoS settings from GuQoS"));
        
        TPtr8 qos(iParkedDefaultQoS->Des());
        GuqosInput (KNifSetDefaultQoS, qos);
        
        delete iParkedDefaultQoS;
        iParkedDefaultQoS = NULL;
        }

	CSpudBinderRef* ref = NULL;
	// Get the binder for the first (default) lower NIF.
	TRAP(err, ref = iBindMan->GetRefL(iPrimaryContextId));
	if (err != KErrNone)
		{
		__FLOG_0(_L("CSpudMan::Start: Error - no context could be found"));
		return err;
		}

    ASSERT(ref->State() == ESpudInactive || ref->State() == ESpudHaveQos);
    ASSERT(ref->State() != ESpudWaitBinderDelete);
    
    if (ref->State() == ESpudWaitBinderDelete)
    	{
	    return KErrNotReady;
    	}
    
	if (ref->State() != ESpudHaveQos)
		{
		__FLOG_0(_L("CSpudMan::Start: No QoS parameters have been set - is GuQoS present?"));
	
		// Sets default QoS parameters because either
      //    1) they weren't supplied by GUQoS - this shouldn't happen
      //    2) or GuQoS has been configured out
#ifdef SYMBIAN_NETWORKING_UMTSR5
		// Sets default R5 QoS parameters because they weren't supplied by GUQoS.
			RPacketQoS::TQoSR5Requested qos;
			ReadDefaultR5QoS(qos);
#else
			RPacketQoS::TQoSR99_R4Requested qos;
			ReadDefaultQoS(qos);
#endif 
// SYMBIAN_NETWORKING_UMTSR5 		
			iPdpFsmInterface.Set(iPrimaryContextId, qos); // ignore any error
		
		}

	// Set default TFT
	TTFTInfo tft;
    iPdpFsmInterface.Set(iPrimaryContextId, tft); // ignore any error
    

	// Have Etel create a context
	ref->SetState(ESpudCreatingPrimary);
	__FLOG_1(_L("CSpudMan::Start: Sending SpudFsm event ECreatePrimaryPDPContext context %d"), iPrimaryContextId);
	TInt rc = iPdpFsmInterface.Input(iPrimaryContextId, ECreatePrimaryPDPContext);
	// TODO: handle errors properly
	ASSERT(rc == KErrNone);
	rc = rc; // Eliminate compiler warning in release builds

	return KErrNone;
    }

/**
Cleanly stop the link.

@param aReason The reason the link is going down
@param aAction The action to take once the link is down
*/
void CSpudMan::Stop(TInt aReason, MNifIfNotify::TAction aAction)
	{
	__FLOG_3(_L("CSpudMan::Stop: reason %d action %d. %d contexts exist."), aReason, aAction, BindMan()->NumContexts());
	ASSERT(BindMan()->NumContexts()); // Primary PDP context is created in the factory.
	
	SetTerminateError(aReason); // Store this error code for use when the SPUD goes down
	if (AreQoSEventsEnabled())
		{
		// Spud was administratively stopped. It can be some time before SPUD signals LinkLayerDown.
		// In the meanwhile, we can receive requests from GUQoS that can interfere with the shutdown.
		// To prevent this, we tell GUQoS to stop bothering SPUD.
		// GUQoS returns the favour by turning off the NIF events within this very call.
		// This means we will not send KNetworkInterfaceDown again, even though we'll try.
		// *********************************************************************************************
		// N.B.: "DEF055691 	GUQoS crashes at shutdown": This defect results in GUQoS crash if the stack
	    // closes flows after the NIF proxy was deleted by GUQoS, as a result of KNetworkInterfaceDown event.
	    // As a temporary workaround, this call is disabled, and KNetworkInterfaceDown event is signalled only
	    // from the destructor. Once this defect is fixed, the following line must be uncommented: 
	    // SendNetworkStatusEvent(KNetworkInterfaceDown, RPacketService::EStatusUnattached);
		//**********************************************************************************************
		}
		
	
	// Send Stop to all lower NIFs that were started but not stopped yet
	TContextId i;
	for (i=0; i < KMaxPdpContexts; ++i)
		{
		StopContext(i, aReason, aAction);
		}		
	// Eventually, the last lower NIF will call LinkLayerDown to trigger the final cleanup
    }

/**
Cleanly stop a context.

@param aReason The reason the link is going down
@param aAction The action to take once the link is down
@param aContextId context
*/
void CSpudMan::StopContext(TContextId aContextId, TInt aReason, MNifIfNotify::TAction aAction)
	{
	CSpudBinderRef* ref = NULL;
	TRAPD(rc, ref = iBindMan->GetRefL(aContextId));
	if (rc == KErrNone && // Binder exists
		ref->IsBound()) // Is bound to a lower NIF.
		{
		
		// Save the Context failure reason
        if (ref->Error() == KErrNone) 
        	{
        	ref->SetError(aReason);
			}

		switch(ref->State()) // NIFs in some states are not eligible for Stop.
			{
			// Context created and NIF started.
			case ESpudStartingPrimaryLowerNif: // Waiting for LinkLayerUp/Down
			case ESpudStartingSecondaryLowerNif:// Waiting for LinkLayerUp/Down
			case ESpudGettingNegQoS: // Waiting for retrieval of negotiated QoS, context activated
			case ESpudUp: // LinkLayerUp received, NIF is up.
			case ESpudFlowOff: // LinkLayerUp received, NIF is up.
			case ESpudSuspended: // LinkLayerUp received, NIF is up.
			case ESpudFlowOffAndSuspended: // LinkLayerUp received, NIF is up.
				__FLOG_2(_L("Lower NIF binder for context[%d] is in state[%S]: Stopping lower NIF."),aContextId,SpudStateToText(ref->State()));
				// Stop the NIF and delete the context via SpudFsm: 
				ref->NifLink()->Stop(aReason, aAction);
				// stay in the Up state so that that GUQoS is notified.
				break;
				
			// Context is being created
			case ESpudCreatingPrimary:
				// SpudFsm will clean up the context and generate a context created event with an error
				rc = iPdpFsmInterface.Input(aContextId, ECancelContextCreate);
				break;
				
			// Context created, but NIF not started
			case ESpudStartingPrimary: 
				ref->SetState(ESpudWaitLinkDown);
				// Any outstanding  SpudFsm request will be cancelled by this delete request.
				rc = iPdpFsmInterface.Input(aContextId, EContextDelete);
				aReason = (KErrNone != aReason) ? aReason : KErrCancel; // Must not be KErrNone.
				SendPrimaryContextCreated(aContextId, aReason);
				break;
				
			case ESpudStartingSecondary: 	
				// Delete the context via SpudFsm
				ref->SetState(ESpudLinkDown); // We'll notify GUQoS from deletion event handler.
				__FLOG_1(_L("Context[%d] created: Sending SpudFsm event EContextDelete"), aContextId);
					
				// Any outstanding  SpudFsm request will be cancelled by this delete request.
				rc = iPdpFsmInterface.Input(aContextId, EContextDelete);
				ASSERT(rc == KErrNone);
				break;
				
			// Can't call Stop: the NIF either not started, or stopped already
			case ESpudContextDelete: // Context deleted by GUQoS, Stop was called.
			case ESpudWaitLinkDown: // Stop was called, waiting for LinkLayerDown
			case ESpudWaitBinderDelete: // LinkLayerDown received, queued for deletion
			case ESpudLinkDown:	    // LinkLayerDown received, not queued for deletion.
				__FLOG_2(_L("Lower NIF binder for context[%d] is in state[%S], and is not eligible for Stop."),aContextId,SpudStateToText(ref->State()));
				break;
				
			case ESpudHaveQos:
			default:
				__FLOG_2(_L("Lower NIF binder for context[%d] is in unexpected state[%S]."),aContextId,SpudStateToText(ref->State()));
				ASSERT(EFalse);
				break;		
			}
		}
	}

/**
Send a packet across the link.
This function should not be called; the Mux is the one that should get the data.

@param aPacket MBuf chain containing packet (ignored)
@param aSource (ignored)

@return Error code, or 1 if packet was queued,
        or KErrNone to flow off sender
*/
TInt CSpudMan::Send(RMBufChain& /*aPacket*/, TAny* /*aSource*/)
	{
    _LIT(KPanicMsg, "CSpudMan");
    User::Panic(KPanicMsg, KErrNotSupported);
	return KErrNotSupported;	// never reached
    }


/**
Receives notification from NIFMAN that the authenticate data is ready.
*/
void CSpudMan::AuthenticateComplete(TInt aResult)
	{
	// Send AuthenticateComplete to all lower NIFs
	TContextId i;
	for (i=0; i < KMaxPdpContexts; ++i)
		{
		CSpudBinderRef* ref = NULL;
		TRAPD(rc, ref = iBindMan->GetRefL(i));
		if (rc == KErrNone)
			{
			ref->NifLink()->AuthenticateComplete(aResult);
			}
		}
	}

void CSpudMan::Restart(CNifIfBase* /*aIf*/)
	{
	// TODO: Is it safe to simply ignore this?
	__FLOG_0(_L("CSpudMan::Restart. Ignored."));
    ASSERT(EFalse);
	}


//*****************************************************************************
// SPUD methods
//*****************************************************************************

/**
Receives progress notifications from lower NIF.

@param aContextId Context ID of lower NIF
@param aStage Progress stage
@param aError Error code
*/
void CSpudMan::IfProgress(TContextId aContextId, TInt aStage, TInt aError)
	{
	__FLOG_3(_L("CSpudMan::IfProgress context ID %d received stage %d error %d"),
			 aContextId, aStage, aError);
 	// Eliminate compiler warnings in release builds
	aContextId = aContextId;
	aStage = aStage;
	aError = aError;
	
    // Drop all progress indications from lower NIFs on the floor because they'll just confuse NIFMAN.
    // SpudMan generates its own progress notifications.
    }

/**
Receives progress notifications from lower NIF.

@param aContextId Context ID of lower NIF
@param aSubConnectionUniqueId Subconnection ID
@param aStage Progress stage
@param aError Error code
*/
void CSpudMan::IfProgress(TContextId aContextId, TSubConnectionUniqueId aSubConnectionUniqueId, TInt aStage, TInt aError)
	{
	__FLOG_4(_L("CSpudMan::IfProgress context ID %d subconnection ID %d received stage %d error %d"),
			 aContextId, aSubConnectionUniqueId, aStage, aError);
 	// Eliminate compiler warnings in release builds
	aContextId = aContextId;
	aSubConnectionUniqueId = aSubConnectionUniqueId;
	aStage = aStage;
	aError = aError;

    // Drop all progress indications from lower NIFs on the floor because they'll just confuse NIFMAN.
    // SpudMan generates its own progress notifications.
    }

/**
Receives notifications from lower NIF to agent.

@param aContextId Valid context ID of lower NIF
@param aEvent Event type
@param aInfo Additional information for event (ignored)
@return KErrNone on success, or KErrNotSupported
*/
TInt CSpudMan::Notification(TContextId aContextId, TNifToAgentEventType aEvent, void* /*aInfo*/)
	{
	__FLOG_2(_L("CSpudMan::Notification context ID %d event ID %d"), aContextId, aEvent);
	switch (aEvent)
		{
	case ENifToAgentEventTsyConfig:
		{
		// Return GPRS context structure to lower NIF
		CSpudBinderRef* ref = NULL;
		TRAPD(rc, ref = iBindMan->GetRefL(aContextId));
		__ASSERT_ALWAYS(rc == KErrNone, Panic());
		ASSERT(ref->IsBound());

	    iPdpFsmInterface.Get(aContextId, iTempGprsContext);
        ref->NifLink()->Notification(EAgentToNifEventTsyConfig, reinterpret_cast<void*>(&iTempGprsContext));
	    return KErrNone;
		}

	case ENifToAgentEventTsyConnectionSpeed:
		{
		// Return connection speed to lower NIF
		CSpudBinderRef* ref = NULL;
		TRAPD(rc, ref = iBindMan->GetRefL(aContextId));
		__ASSERT_ALWAYS(rc == KErrNone, Panic());
		ASSERT(ref->IsBound());

#ifdef SYMBIAN_NETWORKING_UMTSR5
		RPacketQoS::TQoSR5Negotiated params;
#else
		RPacketQoS::TQoSR99_R4Negotiated params;
#endif 
// SYMBIAN_NETWORKING_UMTSR5 

		iPdpFsmInterface.Get(aContextId, params);
		ref->NifLink()->Notification(EAgentToNifEventTsyConnectionSpeed,
	    						 reinterpret_cast<void*>(static_cast<TUint>(params.iMaxRate.iUplinkRate)));

	    return KErrNone;
		}

	default:
		// Just ignore all the other notifications
		__FLOG_0(_L("Ignoring notification"));
		break;
		}

	return KErrNotSupported;
	}

/**
Read a boolean field from the connection settings provider.
Intercepts reads of CommPort and returns the correct value.

@param aContextId Valid context ID of lower NIF
@param aField The name of the field to read
@param aValue On return, contains the value of the field read
@return KErrNone, if successful; otherwise one of the standard Symbian OS error codes
*/
TInt CSpudMan::ReadInt(TContextId aContextId, const TDesC& aField, TUint32& aValue)
	{
	
	//This fix is needed to ensure that multiple PPP channels can be used for different PDP contexts. 
	//The returned value of ECommDbCdmaNaiMobileIp will cause PPP to skip external IP configuration (NCPIP).
	if ( (TPtrC(CDMA_NAI_TYPE) == aField) && (iPrimaryContextId != aContextId) )
		{
		__FLOG_2(_L("CSpudMan::ReadInt context ID %d field  = CDMA_NAI_TYPE - Therefore explicitly setting Value to ECommDbCdmaNaiMobileIp"),
			aContextId, &aField);
		__FLOG(_L("No call to AgentRef Will be make"));
		// Lower NIF is requesting the NAI type
		aValue = ECommDbCdmaNaiMobileIp;
		return KErrNone;
		}
	__FLOG_2(_L("CSpudMan::ReadInt context ID %d field %S"), aContextId, &aField);
	// Read CommDB normally
	return Notify()->ReadInt(aField, aValue);
	}

/**
Read a 8-bit descriptor field from the connection settings provider.
Intercepts reads of CommPort and returns the value returned from ETel.

@param aContextId Valid context ID of lower NIF
@param aField The name of the field to read
@param aValue On return, contains the value of the field read
@return KErrNone, if successful; otherwise one of the standard Symbian OS error codes
*/
TInt CSpudMan::ReadDes(TContextId aContextId, const TDesC& aField, TDes8& aValue)
	{
	__FLOG_2(_L("CSpudMan::ReadDes context ID %d field %S"), aContextId, &aField);
	TBuf<2*KCommsDbSvrMaxColumnNameLength+1> columnName;
	_LIT(KFormatText,"%s\\%s");

	columnName.Format(KFormatText,MODEM_BEARER,MODEM_PORT_NAME);
	if (columnName == aField)
		{
		// Lower NIF is requesting the CSY port name
		// Use the TDes16 version of ReadDes to retrieve the data
		TBuf16<KCommsDbSvrMaxFieldLength> data;
		TInt rc(ReadDes(aContextId, aField, data));
		aValue.Copy(data);
		return rc;
		}

	columnName.Format(KFormatText,MODEM_BEARER,MODEM_CSY_NAME);
	if (columnName == aField)
		{
		// Lower NIF is requesting the CSY file name
		// Use the TDes16 version of ReadDes to retrieve the data
		TBuf16<KCommsDbSvrMaxFieldLength> data;
		TInt rc(ReadDes(aContextId, aField, data));
		aValue.Copy(data);
		return rc;
		}

	// Read CommDB normally
	return Notify()->ReadDes(aField, aValue);
	}

/**
Read a 16-bit descriptor field from the connection settings provider.
Intercepts reads of CommPort and returns the value returned from ETel.

@param aContextId Valid context ID of lower NIF
@param aField The name of the field to read
@param aValue On return, contains the value of the field read
@return KErrNone, if successful; otherwise one of the standard Symbian OS error codes
*/
TInt CSpudMan::ReadDes(TContextId aContextId, const TDesC& aField, TDes16& aValue)
    {
	__FLOG_2(_L("CSpudMan::ReadDes context ID %d field %S"), aContextId, &aField);
	TBuf<2*KCommsDbSvrMaxColumnNameLength+1> columnName;
	_LIT(KFormatText,"%s\\%s");

	columnName.Format(KFormatText,MODEM_BEARER,MODEM_PORT_NAME);
	if (columnName == aField)
		{
		// Lower NIF is requesting the CSY port name
	    iPdpFsmInterface.Get(aContextId, iTempDataChannelV2);
		__FLOG_1(_L("Returning ETel port name %S"), &iTempDataChannelV2.iPort);
		aValue.Copy(iTempDataChannelV2.iPort);
		return KErrNone;
		}

	columnName.Format(KFormatText,MODEM_BEARER,MODEM_CSY_NAME);
	if (columnName == aField)
		{
		// Lower NIF is requesting the CSY file name
	    iPdpFsmInterface.Get(aContextId, iTempDataChannelV2);
		__FLOG_1(_L("Returning ETel CSY name %S"), &iTempDataChannelV2.iCsy);
		aValue.Copy(iTempDataChannelV2.iCsy);
		return KErrNone;
		}

	return Notify()->ReadDes(aField, aValue);
    }

/**
Marks the binder to the lower NIF for asynchronous deletion 

@param aRef the binder
@pre the binder must be bound the lower NIF.
*/
void CSpudMan::DisposeOfBinder(CSpudBinderRef* aRef)
	{
	ASSERT(aRef);
	ASSERT(aRef->IsBound()); // We can only mark - sweep bound instances
	ASSERT(aRef->State() != ESpudWaitBinderDelete);
	aRef->SetState(ESpudWaitBinderDelete);
	iBinderSweeperNotifierCb->Call(); // Queue deletion of marked binders & optional Nifman notification.
	}

/**
Sweeps the set of lower NIF binding, deleting the marked ones. If no contexts remain after,
notifies Nifman that SPUD has gone down.

@param aContextId The ID of the context to delete
@param aReason error code that is passed to Nifman
*/
void CSpudMan::SweepBindersAndNotify()
	{
	const TUint KNumContextsRemaining(BindMan()->SweepBinders());
	if (0 == KNumContextsRemaining)
		{
		SetTerminateError(KErrAbort); // This is a last ditched effort to provide termination
		// error code. We cannot determine in all cases what has caused SPUD to terminate.
		// E.g. if several secondary contexts were deleted by the network, which  of them caused SPUD termination?
		// In such case we say that SPUD is shutting down due to internal event (namely, last context deletion).
		
		__FLOG_3(_L("Last lower NIF has been deleted: Notifying Nifman with action EDisconnect[%d], progress KLinkLayerClosed[%d], reason[%d]"),	
		MNifIfNotify::EDisconnect, KLinkLayerClosed, iTerminateError);
		
		// Once we've notified LinkLayerDown & IfProgress, we are finished. Nifman will delete us any moment after
		// the RunL we are working from returns.
		__FLOG(_L("SPUD is finished, and expects to be deleted by Nifman. Reason: last PDP context has gone down, possibly due to Stop on SPUD."));

		// Tell Nifman clients that SPUD is finished.
		Notify()->LinkLayerDown(iTerminateError, MNifIfNotify::EDisconnect); 
		Notify()->IfProgress(KLinkLayerClosed, iTerminateError);
		}
	else
		{
		__FLOG_1(_L("There are [%d] contexts remaining."), KNumContextsRemaining);
		}
	}

void CSpudMan::SetupSipServerAddrRetrievalL(RPacketContext::TProtocolConfigOptionV2& aPco)
	{
	__FLOG(_L("CSpudMan::SetupSipServerAddrRetrieval - Requesting the P-CSCF address from the PCO buffer"));
	
	TPtr8 pcoPtr(const_cast<TUint8*>(aPco.iMiscBuffer.Ptr()),aPco.iMiscBuffer.Length(),aPco.iMiscBuffer.MaxLength());
	
	// attach TTlv to the buffer
	TTlvStruct<RPacketContext::TPcoId,RPacketContext::TPcoItemDataLength> tlv(pcoPtr,0);
	tlv.AppendItemL(RPacketContext::TPcoId(RPacketContext::EEtelPcktPCSCFAddressRequest), 
		TPtr8(static_cast<TUint8*>(NULL), 0, 0));
	aPco.iMiscBuffer.SetLength(pcoPtr.Length());
	}
	

#ifdef SYMBIAN_NETWORKING_UMTSR5
	
void CSpudMan::SetIMCNSignallingFlagPcoL(RPacketContext::TProtocolConfigOptionV2& aPco)
/**
Put the value for IMCN Signalling flag in the pco buffer if it is set in database

@param PCO IE Buffer
*/
	{
	TBool imcn=EFalse;
	TBuf<2*KCommsDbSvrMaxColumnNameLength+2> columnName;
    _LIT(KFormatText,"%s\\%s");
    columnName.Format(KFormatText,QOS_UMTS_R99_AND_ON_TABLE,GPRS_QOS_IM_CN_SIGNALLING_INDICATOR);
    TRAPD(ret, Notify()->ReadBool(columnName,imcn););
	__FLOG_1(_L("CSpudMan::SetIMCNSignallingFlagPcoL - Requesting IMCN Signalling status from Database: error = %d"),ret);

	 if (imcn && ret==KErrNone )

	 {
	  TPtr8 pcoPtr(const_cast<TUint8*>(aPco.iMiscBuffer.Ptr()),aPco.iMiscBuffer.Length(),aPco.iMiscBuffer.MaxLength());
	  TTlvStruct<RPacketContext::TPcoId,RPacketContext::TPcoItemDataLength> tlv(pcoPtr,0);
	  tlv.AppendItemL(RPacketContext::TPcoId(RPacketContext::EEtelPcktIMCNMSSubsystemSignallingFlag ), 
	  TPtr8(static_cast<TUint8*>(NULL), 0, 0));
	  aPco.iMiscBuffer.SetLength(pcoPtr.Length());
	 }
	}
TBool CSpudMan::GetIMCNSignallingFlagPcoL(RPacketContext::TProtocolConfigOptionV2& aPco)
/**
Get the value for IMCN Signalling from the network pco buffer

@param PCO IE Buffer
*/
	{
	
	__FLOG(_L("CSpudMan::GetIMCNSignallingFlagPcoL - Retrieving the IMCN signalling Flag from the PCO buffer"));
	
	TPtr8 pcoPtr(const_cast<TUint8*>(aPco.iMiscBuffer.Ptr()),aPco.iMiscBuffer.Length(),aPco.iMiscBuffer.MaxLength());
	TTlvStruct<RPacketContext::TPcoId,RPacketContext::TPcoItemDataLength> tlv(pcoPtr,0);
	tlv.ResetCursorPos();

	TInt err = tlv.NextItemL(RPacketContext::EEtelPcktIMCNNetworkSubsystemSignallingFlag,pcoPtr);
	return (err == KErrNone);
	
	}

#endif // SYMBIAN_NETWORKING_UMTSR5
	
	
void CSpudMan::SetSipServerAddrL(const RPacketContext::TProtocolConfigOptionV2& aPco)
	{
	__FLOG(_L("CSpudMan::SetSipServerAddr - Retrieving the P-CSCF address from the PCO buffer"));
	iSipServerAddr.Reset(); //Free all existing entries
	TPtr8 pcoPtr(const_cast<TUint8*>(aPco.iMiscBuffer.Ptr()),aPco.iMiscBuffer.Length(),aPco.iMiscBuffer.MaxLength());
	TTlvStruct<RPacketContext::TPcoId,RPacketContext::TPcoItemDataLength> 
		tlv(pcoPtr,0);
		
	tlv.ResetCursorPos();
	TIp6Addr addr;
	TPtr8 addrPtr(NULL, 0);
	TPckg<TIp6Addr> addrPckg(addr);
		
	while (tlv.NextItemL(RPacketContext::EEtelPcktPCSCFAddress,addrPtr) == KErrNone)
		{
		TInetAddr inetAddr;
		addrPckg.Copy(addrPtr);
		inetAddr.SetAddress(addr);
		TBuf<KMaxInetAddrPrintSize> testbuf;
		inetAddr.Output(testbuf);
		__FLOG_1(_L("CSpudMan::SetSipServerAddr - P-CSCF address ---> %S"),&testbuf);
		if (testbuf.Length()) //ie the address is invalid
			{
			iSipServerAddr.AppendL(inetAddr);
			}
		}
	}
	
void CSpudMan::SetContextTerminationErrorAndStop(TContextId aContextId, TInt aErrorCode)
	{
	__FLOG_2(_L("SetContextTerminateError on StatusEvent: aContextId[%d], aErrorCode[%d]"),	
		aContextId, aErrorCode);
	
	// If there is no error then simply return
	if (KErrNone == aErrorCode) return;
	
    // If secondary context, store error code in individual contexts reference
    // and stop the secondary context
    if(aContextId != iPrimaryContextId)
        {
			StopContext(aContextId, aErrorCode, MNifIfNotify::EDisconnect);
        }
    else
        {
        // This is a problem with the Primary context so stop and disconnect
        // Now save the termination error code if not already set
   		if (iTerminateError == KErrNone)
			{
			iTerminateError = aErrorCode;
			}

		// This may be the first ETel error code so save it
		if (iETelTerminateError == KErrNone)
			{
			iETelTerminateError = aErrorCode;
			}

        // Primary context has a problem so disconnect
        Stop(aErrorCode, MNifIfNotify::EDisconnect);
        }
	}
		
//*************************************************************************
// CLowerNifBinderDeletionCb
// Asynchronous deletion of CSpudBinderRefs and notification to Nifman
//*************************************************************************
// Use Spudman's logging.
// Because we are owned by Spudman, we don't have to worry about the logger being deleted.
#ifdef __FLOG_ACTIVE
#define BINDER_SWEEPER_LOG(x) iSpudMan.x
#else
#define BINDER_SWEEPER_LOG(x)
#endif

// Construct a High-Priority AO that calls into the SPUD
// This will work with any priority AO, but because we are releasing memory and 
// potentially notifying Nifman, we want to run ASAP.
CBinderSweeperNotifierCb::CBinderSweeperNotifierCb(CSpudMan& aSpudMan)
	:
	CAsyncOneShot(CActive::EPriorityHigh), 
	iSpudMan(aSpudMan)
	{
	}

// Queues the deletion callback 
void CBinderSweeperNotifierCb::Call()
	{
	if(!IsActive()) // We can be called again before we had a chance to run.
		{
		BINDER_SWEEPER_LOG(__FLOG(_L("CBinderSweeperNotifierCb: Queueing async deletion of dead lower NIF bindings."));)
		CAsyncOneShot::Call();
		return;
		}
	BINDER_SWEEPER_LOG(__FLOG(_L("CBinderSweeperNotifierCb: Async deletion of dead lower NIF bindings is already queued."));)
	}


// Called by ActiveScheduler.
//
// If the lower NIF deletion is attempted after Nifman deletes the SPUD 
// (from CNifAgentRef::DisconnectionComplete), the lower NIF deletion AO is corrupted in the 
// ActiveScheduler, causing ESock thread to crash. To prevent this, lower NIFs are deleted 
// before signalling LinkLayerDown to Nifman. When a lower NIF signals LinkLayerDown, a callback into Spudman is queued.
// This callback deletes the lower NIFs that are eligible for deletion, and notifies Nifman, if necessary.	*/	
void CBinderSweeperNotifierCb::RunL()
	{
	iSpudMan.SweepBindersAndNotify();
	}
	
