networkcontrol/ipnetworklayer/src/IPProtoCPR.cpp
changeset 0 af10295192d8
child 7 db85996de7c4
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/networkcontrol/ipnetworklayer/src/IPProtoCPR.cpp	Tue Jan 26 15:23:49 2010 +0200
@@ -0,0 +1,999 @@
+// Copyright (c) 2006-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:
+// IPProto Connection Provider implementation
+// 
+//
+
+/**
+ @file
+ @internalComponent
+*/
+
+#define SYMBIAN_NETWORKING_UPS
+
+#include <comms-infras/corecprstates.h>
+#include <comms-infras/corecpractivities.h>
+#include <comms-infras/ss_log.h>
+#include <comms-infras/ss_legacyinterfaces.h>
+#include <comms-infras/ss_datamon_apiext.h>
+#include <es_prot.h> // ESocketTimerPriority/KConnProfile(None/Long/Medium)
+#include <e32def.h>
+#include <es_prot_internal.h>
+
+
+#include "IPProtoCprStates.h"
+#include "IPProtoCPR.h"
+#include "IPProtoMCpr.h"
+#include "IPProtoMessages.h"
+#include "linkcprextensionapi.h"
+
+#include <comms-infras/ss_nodemessages_factory.h>
+#include <comms-infras/ss_msgintercept.h>
+#include <comms-infras/ss_nodemessages_internal.h>
+
+#ifdef SYMBIAN_ADAPTIVE_TCP_RECEIVE_WINDOW
+#include <comms-infras/ss_nodemessages_subconn.h>
+#endif //SYMBIAN_ADAPTIVE_TCP_RECEIVE_WINDOW
+
+#ifdef _DEBUG
+   #include <networking/idletimertest.h>
+#endif
+
+using namespace Messages;
+using namespace MeshMachine;
+using namespace IpProtoCpr;
+using namespace ESock;
+using namespace NetStateMachine;
+using namespace PRActivities;
+
+
+
+//We reserve space for two preallocated activities that may start concurrently on the CPR
+//node: destroy and data client stop.
+static const TUint KDefaultMaxPreallocatedActivityCount = 2;
+static const TUint KMaxPreallocatedActivitySize = sizeof(MeshMachine::CNodeRetryParallelActivity) + sizeof(MeshMachine::APreallocatedOriginators<4>);
+static const TUint KIPProtoCPRPreallocatedActivityBufferSize = KDefaultMaxPreallocatedActivityCount * KMaxPreallocatedActivitySize;
+
+//-=========================================================
+//
+// Activities
+//
+//-=========================================================
+
+namespace IPProtoCprProvisionActivity
+{
+DECLARE_DEFINE_NODEACTIVITY(ECFActivityStoreProvision, IPProtoCprProvision, TCFDataClient::TProvisionConfig)
+	FIRST_NODEACTIVITY_ENTRY(CoreNetStates::TAwaitingProvision, MeshMachine::TNoTag)
+	LAST_NODEACTIVITY_ENTRY(KNoTag, IpProtoCpr::TStoreProvision)
+NODEACTIVITY_END()
+}
+
+
+namespace IPProtoCprConnectionDownActivity
+{
+// In the event that a StopConnection has been issued this activity will not receive
+// a ConnectionDown message. It will be received instead by the running StopConnectionActivity.
+//
+// In the event that the Idle Timer has expired, this node will originate a StopConnection
+// to itself, the StopConnectionActivity will post a ConnectionDown to the origator (this
+// node) and the StopConnectionActivity will go idle. The ConnectionDown message will then
+// be received by this activity.
+
+DECLARE_DEFINE_NODEACTIVITY(ECFActivityGoneDown, IPProtoCprConnectionDown, TCFServiceProvider::TStopped)
+	FIRST_NODEACTIVITY_ENTRY(CoreNetStates::TAwaitingStopped, MeshMachine::TNoTag)
+	LAST_NODEACTIVITY_ENTRY(KNoTag, PRStates::TSendGoneDown)
+NODEACTIVITY_END()
+}
+
+namespace IPProtoCprBinderRequestActivity
+{
+//The reason IPProtoCPR overrides this activity is that IPProto layer doesn't
+//implement non-default SCPRs and although higher levels will ask for them
+//(in QoS scenarios) IPProto will assume the higher levels will do fine
+//with default SCPRs instead. The current QoS solution involves GuQoS and
+//multiplexining channels at IPProto layer.
+DECLARE_DEFINE_CUSTOM_NODEACTIVITY(ECFActivityBinderRequest, IPProtoCprBinderRequest, TCFServiceProvider::TCommsBinderRequest, PRActivities::CCommsBinderActivity::NewL)
+//	FIRST_NODEACTIVITY_ENTRY(CoreNetStates::TAwaitingBinderRequest, CCommsBinderActivity::TNoTagOrWaitForIncomingOrUseExistingBlockedByBinderRequest)
+	FIRST_NODEACTIVITY_ENTRY(CoreNetStates::TAwaitingBinderRequest, CCommsBinderActivity::TNoTagOrWaitForIncomingOrUseExistingDefaultBlockedByBinderRequest)
+	NODEACTIVITY_ENTRY(KNoTag, PRStates::TCreateDataClient, CoreNetStates::TAwaitingDataClientJoin, MeshMachine::TNoTag)
+
+	THROUGH_NODEACTIVITY_ENTRY(KNoTag, CCommsBinderActivity::TProcessDataClientCreation, TTag<CoreStates::KUseExisting>)
+
+	NODEACTIVITY_ENTRY(CoreStates::KUseExisting, CCommsBinderActivity::TSendBinderResponse, CCommsBinderActivity::TAwaitingBindToComplete, MeshMachine::TNoTagOrErrorTag)
+	LAST_NODEACTIVITY_ENTRY(KNoTag, MeshMachine::TDoNothing)
+
+	LAST_NODEACTIVITY_ENTRY(KErrorTag, MeshMachine::TClearError)
+	LAST_NODEACTIVITY_ENTRY(CoreNetStates::KWaitForIncoming, MeshMachine::TRaiseError<KErrNotSupported>)
+NODEACTIVITY_END()
+}
+
+
+
+namespace IPProtoCprDataMonitoringActivity
+{
+DECLARE_DEFINE_NODEACTIVITY(ECFActivityDataMonitoring, IPProtoCprDataMonitoring, TCFDataMonitoringNotification::TDataMonitoringNotification)
+	FIRST_NODEACTIVITY_ENTRY(IpProtoCpr::TAwaitingDataMonitoringNotification, MeshMachine::TNoTag)
+	LAST_NODEACTIVITY_ENTRY(KNoTag, IpProtoCpr::TProcessDataMonitoringNotification)
+NODEACTIVITY_END()
+}
+
+namespace IPProtoCprOpenCloseRouteActivity
+{
+DECLARE_DEFINE_NODEACTIVITY(ECFActivityOpenCloseRoute, IPProtoCprOpenCloseRoute, TCFIPProtoMessage::TOpenCloseRoute)
+	FIRST_NODEACTIVITY_ENTRY(IpProtoCpr::TAwaitingOpenCloseRoute, MeshMachine::TNoTag)
+	LAST_NODEACTIVITY_ENTRY(KNoTag, IpProtoCpr::TDoOpenCloseRoute)
+NODEACTIVITY_END()
+}
+
+namespace IPProtoCprForwardStateChangeActivity
+{
+DECLARE_DEFINE_NODEACTIVITY(ECFActivityForwardStateChange, IPProtoCprForwardStateChange, TCFMessage::TStateChange)
+	NODEACTIVITY_ENTRY(KNoTag, IpProtoCpr::TStoreAndFilterDeprecatedAndForwardStateChange, MeshMachine::TAwaitingMessageState<TCFMessage::TStateChange>, MeshMachine::TNoTag)
+NODEACTIVITY_END()
+}
+
+
+
+
+namespace IPProtoCprLinkDown
+{
+	
+	DECLARE_DEFINE_NODEACTIVITY(ECFActivityGoneDown, IPProtoCprLinkDownOnMesg, TCFControlClient::TGoneDown)
+	// Our Service Provider has gone down unexpectedly (we haven't issued a TStop)
+	NODEACTIVITY_ENTRY(KNoTag, MeshMachine::TDoNothing, TAwaitingGoneDown, MeshMachine::TNoTag)
+	NODEACTIVITY_END()
+}
+
+
+
+namespace IPProtoCprStartActivity
+{
+typedef MeshMachine::TAcceptErrorState<CoreNetStates::TAwaitingDataClientStarted> TAwaitingDataClientStartedOrError;
+
+DECLARE_DEFINE_CUSTOM_NODEACTIVITY(ECFActivityStart, IPProtoCprStart, TCFServiceProvider::TStart, PRActivities::CStartActivity::NewL)
+    FIRST_NODEACTIVITY_ENTRY(IpProtoCpr::TAwaitingStart, CoreNetStates::TNoTagOrBearerPresentBlockedByStop)
+	NODEACTIVITY_ENTRY(CoreNetStates::KBearerPresent, CoreNetStates::TBindSelfToPresentBearer, CoreNetStates::TAwaitingBindToComplete, TTag<CoreNetStates::KBearerPresent>)
+
+	NODEACTIVITY_ENTRY(KNoTag, CoreNetStates::TSendNoBearer, MeshMachine::TAwaitingMessageState<TCFControlProvider::TBearer>, TErrorTagOr<TTag<CoreNetStates::KBearerPresent> >)
+
+	//Start the service provider, use the default cancellation.
+	//Forward TCancel to the service provider, wait for TStarted or TError (via the Error Activity)
+	//When TStarted arrives after TCancel the activity will move to the nearest KErrorTag
+	NODEACTIVITY_ENTRY(CoreNetStates::KBearerPresent, CoreNetStates::TStartServiceProviderRetry, CoreNetStates::TAwaitingStarted, MeshMachine::TNoTagOrErrorTag)
+	LAST_NODEACTIVITY_ENTRY(KErrorTag, IpProtoCpr::TCleanupStart)
+
+	//Start data clients, use the default cancellation.
+	//Forward TCancel to the self, wait for TCFDataClient::TStarted or TError (via the Error Activity)
+	//When TCFDataClient::TStarted arrives after TCancel the activity will move to the nearest KErrorTag
+	NODEACTIVITY_ENTRY(KNoTag, TLinkUpAndTStartSelf, TAwaitingDataClientStartedOrError, MeshMachine::TNoTagOrErrorTag)
+	LAST_NODEACTIVITY_ENTRY(KNoTag, IpProtoCpr::TSendStarted)
+
+	//IPProto layer must stop the lower layer on failure to start as it would detach the lower layer from the idle timer impl.
+	NODEACTIVITY_ENTRY(KErrorTag, IpProtoCpr::TSendStopToSelf, CoreNetStates::TAwaitingStopped, MeshMachine::TErrorTag)
+	LAST_NODEACTIVITY_ENTRY(KErrorTag, IpProtoCpr::TCleanupStart)
+NODEACTIVITY_END()
+}
+
+namespace IPProtoCprClientLeaveActivity
+{ //This activity will wait for ECFActivityBinderRequest to complete
+using namespace  CprClientLeaveActivity;
+DECLARE_DEFINE_CUSTOM_NODEACTIVITY(ECFActivityClientLeave, IPProtoCprClientLeave, Messages::TNodeSignal::TNullMessageId, CClientLeaveActivity::NewL) //May be waiting for both messages
+	FIRST_NODEACTIVITY_ENTRY(CoreStates::TAwaitingClientLeave, MeshMachine::TNoTag)
+	THROUGH_NODEACTIVITY_ENTRY(KNoTag, CprClientLeaveActivity::CClientLeaveActivity::TRemoveClientAndDestroyOrphanedDataClients, CClientLeaveActivity::TNoTagOrSendPriorityToCtrlProvider)
+	NODEACTIVITY_ENTRY(CprStates::KSendPriorityToCtrlProvider, CClientLeaveActivity::TUpdatePriorityForControlProvider, CoreStates::TAwaitingJoinComplete, CClientLeaveActivity::TNoTagOrSendPriorityToServProvider)
+	NODEACTIVITY_ENTRY(CprStates::KSendPriorityToServProvider, CClientLeaveActivity::TUpdatePriorityForServiceProviders, CoreStates::TAwaitingJoinComplete, MeshMachine::TNoTag)
+ 	THROUGH_NODEACTIVITY_ENTRY(KNoTag, CprClientLeaveActivity::CClientLeaveActivity::TSendLeaveCompleteAndSendDataClientIdleIfNeeded, MeshMachine::TNoTag)
+ 	LAST_NODEACTIVITY_ENTRY(KNoTag, IpProtoCpr::TCheckIfLastControlClientLeaving)
+NODEACTIVITY_END()
+}
+
+DECLARE_DEFINE_NODEACTIVITY(ECFIpProtoCprActivityDataClientStatusChange, IPProtoCprDataClientStatusChangeActivity, TCFControlProvider::TDataClientStatusChange)
+	NODEACTIVITY_ENTRY(KNoTag, IpProtoCpr::TProcessDataClientStatusChange, CoreNetStates::TAwaitingDataClientStatusChange, MeshMachine::TNoTag)
+NODEACTIVITY_END()
+
+#ifdef SYMBIAN_ADAPTIVE_TCP_RECEIVE_WINDOW
+namespace IPProtoCprNotificationActivity
+{
+DECLARE_DEFINE_NODEACTIVITY(ECFActivityNotification, IPProtoCprNotification, TCFSubConnControlClient::TPlaneNotification)
+	NODEACTIVITY_ENTRY(KNoTag, CoreNetStates::TPassPlaneEventToControlClients, CoreNetStates::TAwaitingConEvent, MeshMachine::TNoTag)
+NODEACTIVITY_END()
+}
+#endif // SYMBIAN_ADAPTIVE_TCP_RECEIVE_WINDOW
+
+namespace IPProtoCprIoctlActivity
+{
+DECLARE_DEFINE_CUSTOM_NODEACTIVITY(ECFActivityIoctl, IPProtoCprIoctl, TNodeSignal::TNullMessageId, MeshMachine::CNodeParallelMessageStoreActivityBase::NewL)
+    FIRST_NODEACTIVITY_ENTRY(IpProtoCpr::TAwaitingIoctlMessage, MeshMachine::TNoTag)
+    NODEACTIVITY_ENTRY(KNoTag, IpProtoCpr::TForwardToDefaultDataClient, CoreNetStates::TAwaitingRMessage2Processed, MeshMachine::TNoTag)
+    LAST_NODEACTIVITY_ENTRY(KNoTag, CoreStates::TPostToOriginators)
+NODEACTIVITY_END()
+}
+
+namespace IPProtoCprActivities
+{
+DECLARE_DEFINE_ACTIVITY_MAP(activityMap)
+	ACTIVITY_MAP_ENTRY(IPProtoCprForwardStateChangeActivity, IPProtoCprForwardStateChange)
+	ACTIVITY_MAP_ENTRY(IPProtoCprLinkDown, IPProtoCprLinkDownOnMesg) 
+	ACTIVITY_MAP_ENTRY(IPProtoCprProvisionActivity, IPProtoCprProvision)
+	ACTIVITY_MAP_ENTRY(IPProtoCprBinderRequestActivity, IPProtoCprBinderRequest)
+	ACTIVITY_MAP_ENTRY(IPProtoCprDataMonitoringActivity, IPProtoCprDataMonitoring)
+	ACTIVITY_MAP_ENTRY(IPProtoCprConnectionDownActivity, IPProtoCprConnectionDown)
+	ACTIVITY_MAP_ENTRY(IPProtoCprOpenCloseRouteActivity, IPProtoCprOpenCloseRoute)
+	ACTIVITY_MAP_ENTRY(IPProtoCprStartActivity, IPProtoCprStart)
+	ACTIVITY_MAP_ENTRY(IPProtoCprDataClientStatusChangeActivity, IPProtoCprDataClientStatusChangeActivity)
+	ACTIVITY_MAP_ENTRY(PRDataClientIdleActivity, PRDataClientIdle)
+	ACTIVITY_MAP_ENTRY(IPProtoCprClientLeaveActivity, IPProtoCprClientLeave)
+	ACTIVITY_MAP_ENTRY(IPProtoCprIoctlActivity, IPProtoCprIoctl)
+#ifdef SYMBIAN_ADAPTIVE_TCP_RECEIVE_WINDOW
+	ACTIVITY_MAP_ENTRY(IPProtoCprNotificationActivity, IPProtoCprNotification)
+#endif // SYMBIAN_ADAPTIVE_TCP_RECEIVE_WINDOW
+ACTIVITY_MAP_END_BASE(CprActivities, coreCprActivities)
+}
+
+//-=========================================================
+//
+// CIPProtoConnectionProvider methods
+//
+//-=========================================================
+CIPProtoConnectionProvider* CIPProtoConnectionProvider::NewL(CConnectionProviderFactoryBase& aFactory)
+    {
+    CIPProtoConnectionProvider* prov = new (ELeave) CIPProtoConnectionProvider(aFactory,IPProtoCprActivities::activityMap::Self());
+    CleanupStack::PushL(prov);
+    prov->ConstructL();
+    CleanupStack::Pop(prov);
+    return prov;
+    }
+
+CIPProtoConnectionProvider::~CIPProtoConnectionProvider()
+    {
+    LOG_NODE_DESTROY(KIPProtoCprTag, CIPProtoConnectionProvider);
+
+    CancelTimer();
+    delete iTimer;
+    
+    iNodeLocalExtensions.Close();
+    }
+
+CIPProtoConnectionProvider::CIPProtoConnectionProvider(CConnectionProviderFactoryBase& aFactory, const MeshMachine::TNodeActivityMap& aActivityMap) :
+	CCoreConnectionProvider(aFactory,aActivityMap),
+	ALegacySubConnectionActiveApiExt(this),
+	TIfStaticFetcherNearestInHierarchy(this),
+    iDataMonitoringConnProvisioningInfo(&iDataVolumes, &iThresholds)
+    {
+    LOG_NODE_CREATE(KIPProtoCprTag, CIPProtoConnectionProvider);
+    }
+
+void CIPProtoConnectionProvider::ConstructL()
+    {
+    iTimer = COneShotTimer::NewL(ESocketTimerPriority, this);
+
+    ADataMonitoringProvider::ConstructL();
+    CCoreConnectionProvider::ConstructL(KIPProtoCPRPreallocatedActivityBufferSize);
+    }
+
+void CIPProtoConnectionProvider::ReturnInterfacePtrL(ADataMonitoringProtocolReq*& aInterface)
+	{
+	aInterface = this;
+	}
+
+void CIPProtoConnectionProvider::ReturnInterfacePtrL(MLinkCprApiExt*& aInterface) 	  	 
+	{ 	  	 
+	//Get the extension from the Access Point Config, it must be there by now (constructed on provision) 	  	 
+	//We are the only node ever accessing the interface, this is why we can safely return it as non-const. 	  	 
+	CLinkCprExtensionApi* ext = const_cast<CLinkCprExtensionApi*>(static_cast<const CLinkCprExtensionApi*>(AccessPointConfig().FindExtension(CLinkCprExtensionApi::TypeId()))); 	  	 
+	ASSERT(ext); //Udeb 	  	 
+	User::LeaveIfError(ext? KErrNone : KErrCorrupt); //Urel 	  	 
+	aInterface = ext; 	  	 
+	}
+
+
+void CIPProtoConnectionProvider::ReturnInterfacePtrL(ESock::MLegacyControlApiExt*& aInterface)
+	{
+	aInterface = this;
+	}
+
+void CIPProtoConnectionProvider::ReturnInterfacePtrL(ESock::ALegacySubConnectionActiveApiExt*& aInterface)
+	{
+	aInterface = this;
+	}
+
+/**
+Retrieves the ALegacyEnumerateSubConnectionsApiExt implementation
+*/
+void CIPProtoConnectionProvider::ReturnInterfacePtrL(ESock::ALegacyEnumerateSubConnectionsApiExt*& aInterface)
+    {
+    aInterface = this;
+    }
+
+
+void CIPProtoConnectionProvider::EnumerateSubConnections(CLegacyEnumerateSubConnectionsResponder*& aResponder)
+	{
+	TInt count = CountClients<TDefaultClientMatchPolicy>(TClientType(TCFClientType::EData, TCFClientType::EStarted));
+
+	/*
+	  Plus one for to match legacy behaviour. The extra subconnection is there to
+	  represent the connectino and all its subconnections as a whole.
+
+	  So subconnection array is accessed as:
+	    [0] = Entire connection
+		[1] = Default subconnection
+		[2] = non-default subconnection ...
+		...
+	*/
+	count += 1;
+	CLegacyEnumerateSubConnectionsResponder::CompleteClient(aResponder, count);
+	}
+
+void CIPProtoConnectionProvider::ReceivedL(const TRuntimeCtxId& aSender, const TNodeId& aRecipient, TSignatureBase& aMessage)
+    {
+	ESOCK_DEBUG_MESSAGE_INTERCEPT(aSender, aMessage, aRecipient);
+    TNodeContext<CIPProtoConnectionProvider> ctx(*this, aMessage, aSender, aRecipient);
+   	Received(ctx);
+    User::LeaveIfError(ctx.iReturn);
+	}
+
+void CIPProtoConnectionProvider::Received(MeshMachine::TNodeContextBase& aContext)
+    {
+    Messages::TNodeSignal::TMessageId noPeerIds[] = {
+        TCFFactory::TPeerFoundOrCreated::Id(),
+        TCFPeer::TJoinRequest::Id(),
+        //TDataMonitoringInternal no-peer as Flow sending directly.
+        TCFDataMonitoringNotification::TDataMonitoringNotification::Id(),
+        TCFIPProtoMessage::TOpenCloseRoute::Id(),
+        Messages::TNodeSignal::TMessageId()
+        };
+
+    MeshMachine::AMMNodeBase::Received(noPeerIds, aContext);
+	MeshMachine::AMMNodeBase::PostReceived(aContext);
+	}
+
+void CIPProtoConnectionProvider::LinkUp()
+	{
+	ESOCK_EXTLOG_VAR( (KESockConnectionTag, KIPProtoCprSubTag, _L8("CIPProtoConnectionProvider %x:\tLinkUp()"), this) );
+	ASSERT(!iLinkUp);
+	iLinkUp = ETrue;
+	iLastControlClientsCount = ControlClientsCount();
+
+	TTime now;
+	now.UniversalTime();
+	iStartTime = now;
+	}
+
+void CIPProtoConnectionProvider::LinkDown()
+	{
+	ESOCK_EXTLOG_VAR( (KESockConnectionTag, KIPProtoCprSubTag, _L8("CIPProtoConnectionProvider %x:\tLinkDown()"), this) );
+
+	iLinkUp = EFalse;
+	CancelTimer();
+	}
+
+void CIPProtoConnectionProvider::OpenRoute()
+	{
+	if (iTimerExpired)
+		{
+		return;
+		}
+	iRouteCount++;
+	ESOCK_EXTLOG_VAR( (KESockConnectionTag, KIPProtoCprSubTag, _L8("CIPProtoConnectionProvider %x:\tOpenRoute() count %d"), this, iRouteCount) );
+	}
+
+void CIPProtoConnectionProvider::CloseRoute()
+	{
+	ESOCK_EXTLOG_VAR( (KESockConnectionTag, KIPProtoCprSubTag, _L8("CIPProtoConnectionProvider %x:\tCloseRoute() count %d timer expired: %d"), this, iRouteCount-1, iTimerExpired) );
+	if (iTimerExpired)
+		{
+		return;
+		}
+	ASSERT(iRouteCount > 0);
+	if (--iRouteCount == 0 && !iTimer->IsActive())
+		{
+		// if the number of calls to CloseRoute() matches those to OpenRoute(), then ensure that
+		// the idle timer is running.
+		TTimerType newMode;
+
+		if ( (iRouteCountStretchOne)
+			&& (iTickThreshold[iTimerMode] != (TInt)KMaxTUint32)
+			)
+			{
+			// Note that there is a slim possiblility that the OpenRoute / CloseRoute event pair
+			// occured too quickly and that a TimerComplete event did not occur to check iRouteCount.
+			// To account for the OpenRoute / CloseRoute event pair artificially lengthen the iRouteCount.
+			// If the current timer is disabled then the OpenRoute / CloseRoute event pair
+			// would never have been detected so then dont stretch the event.
+			newMode = DecideTimerMode(1);
+			}
+		else
+			{
+			newMode = DecideTimerMode(iRouteCount);
+			}
+
+		if (newMode != ETimerUnknown)
+			SetTimerMode(newMode);
+		else
+			ResetTimer();
+		}
+
+	}
+
+void CIPProtoConnectionProvider::TimerComplete(TInt /*aError*/)
+	{
+	ESOCK_EXTLOG_VAR( (KESockConnectionTag, KIPProtoCprSubTag, _L8("CIPProtoConnectionProvider %x:\tTimer Complete %d/%d ticks, Mode %d"), this, iExpiredTicks+1, iTickThreshold[iTimerMode], iTimerMode) );
+
+	if (iTimerMode == ETimerImmediate)
+		{
+		ESOCK_EXTLOG_VAR( (KESockConnectionTag, KIPProtoCprSubTag, _L8("CIPProtoConnectionProvider %x:\tIdle timeout completed - stopping interface"), this) );
+		TimerExpired();
+		return;
+		}
+
+	ASSERT(iLinkUp);
+
+	// reset the iRouteCountStretchOne
+	iRouteCountStretchOne = EFalse;
+
+	// Determine if we need to alter the timer mode
+	TTimerType newMode = DecideTimerMode(iRouteCount);
+	iConnectionControlActivity = EFalse; // (do not reset before DecideTimerMode())
+
+	if (0 == iRouteCount)
+		{
+		// Note that there is a slim possiblility that the OpenRoute / CloseRoute event pair
+		// occured too quickly and that a TimerComplete event did not occur to check iRouteCount.
+		// If this occurs then the newMode selected here will be incorrect.
+		// Extend iRouteCount if iRouteCount > 0 -> iRouteCount = 0 is seen before the next timer event
+		iRouteCountStretchOne = ETrue;
+		}
+
+	// Also a similar issue of connection Start/Attach type activity occuring too quickly to
+	// be noticed by iConnectionControlActivity might be present.
+	// TODO create a test and solution to prove the connection Start/Attach type activity.
+
+	// set new timer mode if required
+	if (newMode != ETimerUnknown)
+		SetTimerMode(newMode);
+
+	if (iPeriodActivity)
+		{
+		iPeriodActivity = EFalse;
+
+		// Reset the timer on packet activity (if the timer mode hasn't just been changed).
+		// (Should this reset only the Long timer, or should it reset the timer in all modes ?)
+
+		if (newMode == ETimerUnknown)
+			{
+			ESOCK_EXTLOG_VAR( (KESockConnectionTag, KIPProtoCprSubTag, _L8("CIPProtoConnectionProvider %x:\tTimer reset due to packet activity"), this) );
+			ResetTimer();
+			}
+		}
+	else
+		{
+		if (newMode == ETimerUnknown)
+			{
+			// No Activity and no change in timer state, check if timer has expired
+			// (checking first for a value of KMaxTUint32, which means the timer is disabled).
+
+			if (iTickThreshold[iTimerMode] != (TInt)KMaxTUint32)
+				{
+				if (iTickThreshold[iTimerMode] <= ++iExpiredTicks)
+					{
+					ESOCK_EXTLOG_VAR( (KESockConnectionTag, KIPProtoCprSubTag, _L8("CIPProtoConnectionProvider %x:\tIdle timeout completed"), this) );
+					TimerExpired();
+					}
+				else
+					{
+					StartNextTick();
+					}
+				}
+			}
+		}
+	}
+
+CIPProtoConnectionProvider::TTimerType CIPProtoConnectionProvider::DecideTimerMode(TInt aRouteCount)
+	{
+	TTimerType newMode = ETimerUnknown;
+	TInt currentControlClientsCount = ControlClientsCount();
+	if (currentControlClientsCount > iLastControlClientsCount)
+		{
+		iConnectionControlActivity = ETrue;
+		}
+	ESOCK_EXTLOG_VAR( (KESockConnectionTag, KIPProtoCprSubTag, _L8("CIPProtoConnectionProvider %x:\tDecideTimerMode() currentControlClientsCount %d iLastControlClientsCount %d iRouteCount %d"), this, currentControlClientsCount, iLastControlClientsCount, iRouteCount) );
+	iLastControlClientsCount = currentControlClientsCount;
+
+	switch (iTimerMode)
+		{
+	case ETimerShort:
+		if (aRouteCount > 0) // any Flows or ESock Sessions?
+			{
+			ESOCK_EXTLOG_VAR( (KESockConnectionTag, KIPProtoCprSubTag, _L8("CIPProtoConnectionProvider %x:\tTimer mode changed from Short to Long due to presence of protocol flows"), this) );
+			newMode = ETimerLong;
+			}
+		else if (iLastControlClientsCount > 0) // any new Control Clients attached?
+			{
+			ESOCK_EXTLOG_VAR( (KESockConnectionTag, KIPProtoCprSubTag, _L8("CIPProtoConnectionProvider %x:\tTimer mode changed from Short to Medium due to presence of control providers"), this) );
+			newMode = ETimerMedium;
+			}
+		break;
+
+	case ETimerMedium:
+		if (aRouteCount > 0)
+			{
+			ESOCK_EXTLOG_VAR( (KESockConnectionTag, KIPProtoCprSubTag, _L8("CIPProtoConnectionProvider %x:\tTimer mode changed from Medium to Long due to presence of flows"), this) );
+			newMode = ETimerLong;
+			}
+		else if (iLastControlClientsCount == 0)
+			{
+			ESOCK_EXTLOG_VAR( (KESockConnectionTag, KIPProtoCprSubTag, _L8("CIPProtoConnectionProvider %x:\tTimer mode changed from Medium to Short due to absence of flows and control providers"), this) );
+			newMode = ETimerShort;
+			}
+		else if (iConnectionControlActivity && iTickThreshold[iTimerMode] != (TInt)KMaxTUint32)
+			{
+			// there has been connection Start/Attach type activity, so reset medium timer
+			ESOCK_EXTLOG_VAR( (KESockConnectionTag, KIPProtoCprSubTag, _L8("CIPProtoConnectionProvider %x:\tMedium timer reset due to connection control activity"), this) );
+			newMode = ETimerMedium;
+			}
+		else if (0 == aRouteCount && iTickThreshold[iTimerMode] == (TInt)KMaxTUint32 && iTickThreshold[ETimerShort] != (TInt)KMaxTUint32)
+			{
+			// There are no sockets and the current timer is disabled but the short timer is set
+			ESOCK_EXTLOG_VAR( (KESockConnectionTag, KIPProtoCprSubTag, _L8("CIPProtoConnectionProvider %x:\tTimer set to Short because there are no Sockets and Long or Medium timer is disabled"), this) );
+			newMode = ETimerShort;
+			}
+		break;
+
+	case ETimerLong:
+		if (0 == aRouteCount)
+			{
+			if (iLastControlClientsCount > 0)
+				{
+				ESOCK_EXTLOG_VAR( (KESockConnectionTag, KIPProtoCprSubTag, _L8("CIPProtoConnectionProvider %x:\tTimer mode changed from Long to Medium due to presence of control providers"), this) );
+				newMode = ETimerMedium;
+				}
+			else
+				{
+				ESOCK_EXTLOG_VAR( (KESockConnectionTag, KIPProtoCprSubTag, _L8("CIPProtoConnectionProvider %x:\tTimer mode changed from Long to Short due to absence of flows and control providers"), this) );
+				newMode = ETimerShort;
+				}
+			}
+		break;
+
+	default:
+		break;
+		}
+
+	return (newMode);
+	}
+
+/**
+ Start the next one second tick for the Idle Timer.
+ As the Idle Timer is implemented as a repeated one second timeout rather than a single timeout period,
+ this routine is necessary to ensure overall accuracy.  The period of each "one second" tick
+ is adjusted slightly to compensate for any accumulated inaccuracies.
+ */
+void CIPProtoConnectionProvider::StartNextTick()
+	{
+	/*
+	The inactivity timeout period is made up of a number of successive
+	one second timer periods up to the desired inactivity timeout.
+	This is done because certain operations are needed every second.
+	However, this can result in cumulative errors in the final timeout
+	period.  An attempt is made here to keep the final timeout period
+	accurate by adjusting the duration of a timer tick every so often
+	to compensate for any observed drift.  This is only best-effort
+	synchronisation which ignores drift that seems way out - as could
+	happen if user altered system time, for example,
+	*/
+
+	if (iExpiredTicks % KTimerCorrectionPeriod == 0)
+		{
+		TTime currentTime;
+		currentTime.HomeTime();
+
+		// Time interval for timer synch & high limit for validity of observed timer drift
+		const TTimeIntervalMicroSeconds KTimeCheckInterval(KTimerCorrectionPeriod * KTimerTick);
+
+		iDriftCheckTime += KTimeCheckInterval;
+
+		// Only act on latest timer drift if it's within sensible limits.
+		// Might not be if user has reset system time, for example.
+		if ( currentTime > iDriftCheckTime )
+			{
+			TInt64 t = currentTime.MicroSecondsFrom(iDriftCheckTime).Int64();
+			if ( t < KTimeCheckInterval.Int64())
+				iTotalTimerDrift += I64LOW(t);
+			}
+		else
+			{
+			TInt64 t = iDriftCheckTime.MicroSecondsFrom(currentTime).Int64();
+ 			if (t < KTimerTick-KMinTimerTick)
+				iTotalTimerDrift -= I64LOW(t);
+			}
+
+		iDriftCheckTime = currentTime;
+
+		if (iTotalTimerDrift > KTimerTick - KMinTimerTick)
+			TimerAfter(KMinTimerTick);
+		else if (iTotalTimerDrift > 0)
+			TimerAfter(KTimerTick - iTotalTimerDrift);
+		else
+			TimerAfter(KTimerTick);
+		}
+	else
+		TimerAfter(KTimerTick);
+	}
+
+void CIPProtoConnectionProvider::SetTimers(TUint32 aShortTimer, TUint32 aMediumTimer, TUint32 aLongTimer)
+	{
+	ESOCK_EXTLOG_VAR( (KESockConnectionTag, KIPProtoCprSubTag, _L8("CIPProtoConnectionProvider %x:\tSetTimers(aShortTimer %ds, aMediumTimer %ds, aLongTimer %ds)"), this, aShortTimer, aMediumTimer, aLongTimer) );
+
+	// Commsdat field: LAST_SESSION_CLOSED_TIMEOUT
+	iTickThreshold[ETimerShort] = aShortTimer;
+	// Commsdat field: LAST_SOCKET_CLOSED_TIMEOUT
+	iTickThreshold[ETimerMedium] = aMediumTimer;
+	// Commsdat field: LAST_SOCKET_ACTIVITY_TIMEOUT
+	iTickThreshold[ETimerLong] = aLongTimer;
+	iTickThreshold[ETimerImmediate] = 0;
+	}
+
+void CIPProtoConnectionProvider::ResetTimer()
+    {
+	/**
+	Restart the Idle Timer.
+	Used when switching the timer into a different mode of operation.
+	*/
+
+    // Initial the iRouteCountStretchOne boolean
+    // Extend iRouteCount if a iRouteCount > 0 is seen before the next timer event
+    iRouteCountStretchOne = ETrue;
+
+#ifdef ESOCK_EXTLOG_ACTIVE
+	TBuf8<9> mode;	// enough for "Immediate"
+	TInt len(0);
+	switch(iTimerMode)
+		{
+		case ETimerLong:
+			mode = _L8("Long");
+			len = iTickThreshold[ETimerLong];
+			break;
+
+		case ETimerMedium:
+			mode = _L8("Medium");
+			len = iTickThreshold[ETimerMedium];
+			break;
+
+		case ETimerShort:
+			mode = _L8("Short");
+			len = iTickThreshold[ETimerShort];
+			break;
+
+		case ETimerImmediate:
+			mode = _L8("Immediate");
+			break;
+
+		default:
+			mode = _L8("Unknown");
+		}
+	ESOCK_EXTLOG_VAR( (KESockConnectionTag, KIPProtoCprSubTag, _L8("CIPProtoConnectionProvider %x:\tTimer mode set to %S (%d ticks)"), this, &mode, len) );
+#endif
+
+	// if we are not in the packet activity monitoring mode, reset the activity flag.
+	if (iTimerMode != ETimerLong)
+		{
+		iPeriodActivity = EFalse;
+		}
+
+	if ( !iLinkUp || iTimer->IsActive() )
+
+		{
+		return;
+		}
+
+	iExpiredTicks = 0;
+	iTotalTimerDrift = 0;
+	iDriftCheckTime.HomeTime();
+	// Only start the timer if it is not disabled (i.e. KMaxTUint32)
+	// Defensive check against ETimerUnknown.
+	if ( iTimerMode != ETimerUnknown && iTickThreshold[iTimerMode] != (TInt)KMaxTUint32 )
+		{
+		TimerAfter(KTimerTick);
+		}
+	}
+
+void CIPProtoConnectionProvider::DisableTimers()
+	{
+	if(0 == iTimerDisableCount)
+		{
+		CancelTimer();
+		}
+	iTimerDisableCount++;
+	}
+
+void CIPProtoConnectionProvider::EnableTimers()
+	{
+	--iTimerDisableCount;
+	if(0 == iTimerDisableCount)
+		{
+		ResetTimer();
+		}
+	}
+
+void CIPProtoConnectionProvider::CancelTimer()
+    {
+    if (iTimer)
+    	{
+    	iTimer->Cancel();
+    	}
+    }
+
+void CIPProtoConnectionProvider::StopConnection()
+	{
+	if (!iTimerExpired)
+		{
+		iTimerExpired = ETrue;
+		CancelTimer();
+		if (CountActivities(ECFActivityStop) == 0)
+			{
+			RClientInterface::OpenPostMessageClose(Id(), TNodeCtxId(ECFActivityStop, Id()), TCFServiceProvider::TStop(KErrTimedOut).CRef());
+			}
+		}
+	}
+
+void CIPProtoConnectionProvider::TimerExpired()
+	{
+	ESOCK_EXTLOG_VAR( (KESockConnectionTag, KIPProtoCprSubTag, _L8("CIPProtoConnectionProvider %x:\tTimerExpired()"), this) );
+	StopConnection();
+	}
+
+TInt CIPProtoConnectionProvider::ControlClientsCount()
+	{
+	return CountClients<TDefaultClientMatchPolicy>(
+		TClientType(TCFClientType::ECtrl),
+		TClientType(TCFClientType::ECtrl, TCFClientType::EMonitor)
+		);
+	}
+
+void CIPProtoConnectionProvider::SetUsageProfile(TInt aProfile)
+	{
+	ESOCK_EXTLOG_VAR( (KESockConnectionTag, KIPProtoCprSubTag, _L8("CIPProtoConnectionProvider %x:\tSetUsageProfile(%d)"), this, aProfile) );
+
+	TInt currentControlClientsCount = ControlClientsCount();
+
+	switch (aProfile)
+		{
+	case KConnProfileMedium:
+		if (currentControlClientsCount == 0)
+			{
+			// Move from short to medium timer
+			if (iTimerMode == ETimerShort)
+				{
+				ESOCK_EXTLOG_VAR( (KESockConnectionTag, KIPProtoCprSubTag, _L8("CIPProtoConnectionProvider %x:\tUsage profile %d - timer mode set to Medium"), this, aProfile) );
+				SetTimerMode(ETimerMedium);
+				}
+			}
+		break;
+
+	case KConnProfileNone:
+		if (currentControlClientsCount == 0 && iRouteCount == 0)
+			{
+			if (iTimerMode == ETimerMedium)
+				{
+				// Moving from medium to short timer
+				ESOCK_EXTLOG_VAR( (KESockConnectionTag, KIPProtoCprSubTag, _L8("CIPProtoConnectionProvider %x:\tUsage profile %d - timer mode set to Short"), this, aProfile) );
+				SetTimerMode(ETimerShort);
+				}
+			else if ((iTimerMode == ETimerUnknown) && !iLinkUp)
+				{
+				ESOCK_EXTLOG_VAR( (KESockConnectionTag, KIPProtoCprSubTag, _L8("CIPProtoConnectionProvider %x:\tUsage profile %d - stopping interface"), this, aProfile) );
+				}
+			}
+
+		break;
+
+	default:
+		ASSERT(0);
+		}
+	}
+
+void CIPProtoConnectionProvider::SetTimerMode(TTimerType aTimerMode)
+	{
+	iTimerMode = aTimerMode;
+	ResetTimer();
+	};
+
+void CIPProtoConnectionProvider::TimerAfter(TInt aInterval)
+	{
+	ASSERT(iTimer);
+	iTimer->After(aInterval);
+	}
+
+TInt CIPProtoConnectionProvider::ControlL(TUint aOptionLevel, TUint aOptionName, TDes8& aOption, MPlatsecApiExt* aPlatsecItf)
+	{
+	switch(aOptionLevel)
+		{
+#ifdef _DEBUG
+		// We're only servicing 'testing' control options here.  They used
+		// to be serviced by Dummy NIF but now the idle timers are
+		// associated with the IPProto layer rather than the link layer.
+	case KCOLInterface:
+		if (aOption.Length() != sizeof(TInt))
+			{
+			return KErrArgument;
+			}
+		switch(aOptionName)
+			{
+		case KTestSoDummyNifSetLastSessionClosedTimeout:
+			iTickThreshold[ETimerShort] = *(reinterpret_cast<const TInt*>(aOption.Ptr()));
+			break;
+
+		case KTestSoDummyNifSetLastSocketClosedTimeout:
+			iTickThreshold[ETimerMedium] = *(reinterpret_cast<const TInt*>(aOption.Ptr()));
+			break;
+
+		case KTestSoDummyNifSetLastSocketActivityTimeout:
+			iTickThreshold[ETimerLong] = *(reinterpret_cast<const TInt*>(aOption.Ptr()));
+			break;
+
+		default:
+			return KErrNotSupported;
+			}
+		break;
+#endif // _DEBUG
+
+	case KCOLProvider:
+		switch(aOptionName)
+			{
+		case KConnDisableTimers:
+			{
+			if(!aPlatsecItf->HasCapability(ECapabilityNetworkControl))
+				{
+				return KErrPermissionDenied;
+				}
+
+			if (aOption.Length() != sizeof(TBool))
+				{
+				return KErrArgument;
+				}
+
+			TBool disable = *reinterpret_cast<const TBool*>(aOption.Ptr());
+			if(disable)
+				{
+				DisableTimers();
+				}
+			else
+				{
+				EnableTimers();
+				}
+			break;
+			}
+		case KConnGetInterfaceName:
+			{
+			if(aOption.Length() != sizeof(TConnInterfaceName))
+				{
+				return KErrArgument;
+				}
+
+ 			TConnInterfaceName* connItfName;
+			connItfName = reinterpret_cast<TConnInterfaceName*>(const_cast<TUint8*>(aOption.Ptr()));
+
+			XInterfaceNames* itfNames = static_cast<XInterfaceNames*>(const_cast<Meta::SMetaData*>(AccessPointConfig().FindExtension(XInterfaceNames::TypeId())));
+
+			if(!itfNames)
+				{
+				return KErrNotFound;
+				}
+
+			// Interface indices are 1-based we so perform subtract 1 to get the correct
+			// name from the store.
+			TUint ret = itfNames->InterfaceName(connItfName->iIndex - 1, connItfName->iName);
+
+			return ret;
+			}
+		default:
+			return KErrNotSupported;
+			}
+		break;
+
+	default:
+		return KErrNotSupported;
+		}
+
+	return KErrNone;
+	}
+
+void CIPProtoConnectionProvider::ForceCheckShortTimerMode()
+/**
+ * This method allow to force to check if it's possible to switch
+ * 	to the TimerMode "Short" without waiting for the next tick.
+ *
+ * The "IdleTimer" inside IPProtoCpr can work in 3 different TimerMode:
+ * 	1) Short 2) Medium 3) Long.
+ * Depending on the Number of CtrlClients attached and on the
+ * 	Activity in the lower planes, the Timer change is mode.
+ * A problem poped-out when there are no more CtrlClient and there is no
+ * 	Activity: the timer needs to switch to "Short" and, if nothing happens
+ * 	in the meanwhile that the Short timeout finish, send a "StopSelf" message.
+ * BUT this is based on the "count" of the number of CtrlClient and this
+ * 	"count" is done ONLY every Tick. That, in this case, has 1 second freq.
+ * This means that in many situation the Timer lose almost 1 second BEFORE
+ * 	to recognize that it has to switch to Short mode.
+ *
+ * This method allow to "force" the check to see if it's the right moment
+ * 	to switch to Short mode and, if it is the case, it does so.
+ * We overriden the activity "ClientLeaveActivity" so this method is
+ * 	called when a "ClientLeave" message is processed by this node.
+ *
+ * It's clear that this is just a patch: the whole timer needs a refactoring.
+ */
+	{
+	// If the number of ControlClient goes to "0", it needs to
+	// 	force to switch the TimerMode to "ShortTimeout".
+	// This is to avoid to waste *1 Tick* (waiting for the next one)
+	//	to recognize that the number of ControlClient is "0".
+	//
+	// We do exactly what it's done when a Tick is complete:
+	//	this will switch the Mode to Short Timeout.
+	if ( iLastControlClientsCount >= 1 &&	// - If there was at least 1 Control Clients
+			iTimerMode == ETimerMedium &&	// - AND the TimerMode is on Medium
+			iTimerExpired == EFalse &&		// - AND The time is not ALREADY Expired
+			ControlClientsCount() == 0		// - AND There are no more Control Clients Attached
+			)
+		{
+		ESOCK_EXTLOG_VAR( (KESockConnectionTag, KIPProtoCprSubTag, _L8("CIPProtoConnectionProvider %x:\tTimer mode FORCED to Short due to absence of flows and control providers"), this) );
+		iLastControlClientsCount = 0;
+		TTimerType newMode = ETimerShort;
+
+		iConnectionControlActivity = EFalse;
+
+		iTimerMode = newMode;
+		iPeriodActivity = EFalse;
+		iExpiredTicks = 0;
+		iTotalTimerDrift = 0;
+		iDriftCheckTime.HomeTime();
+
+		// The timer may never have been started if no sockets were ever opened and the "medium"
+		// and "long" timers were infinite. So we make sure that it is running.
+		ResetTimer();
+		}
+	}
+
+void CIPProtoConnectionProvider::GetSubConnectionInfo(TSubConnectionInfo &aInfo)
+	{
+	aInfo.iTimeStarted = iStartTime;
+	}
+
+//
+// CIPProtoConnectionProvider::COneShotTimer
+//
+CIPProtoConnectionProvider::COneShotTimer* CIPProtoConnectionProvider::COneShotTimer::NewL(TInt aPriority, CIPProtoConnectionProvider* aOwner)
+	{
+	COneShotTimer* self = new (ELeave)COneShotTimer(aPriority, aOwner);
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop(self);
+	return self;
+	}
+CIPProtoConnectionProvider::COneShotTimer::COneShotTimer(TInt aPriority, CIPProtoConnectionProvider* aOwner)
+	: CTimer(aPriority), iOwner(aOwner)
+	{
+	CActiveScheduler::Add(this);
+	}
+
+void CIPProtoConnectionProvider::COneShotTimer::RunL()
+	{
+	iOwner->TimerComplete(iStatus.Int());
+	}
+
+void CIPProtoConnectionProvider::COneShotTimer::ConstructL()
+	{
+	CTimer::ConstructL();
+	}
+