diff -r 000000000000 -r af10295192d8 networkcontrol/ipnetworklayer/src/IPProtoCPR.cpp --- /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 +#include +#include +#include +#include +#include // ESocketTimerPriority/KConnProfile(None/Long/Medium) +#include +#include + + +#include "IPProtoCprStates.h" +#include "IPProtoCPR.h" +#include "IPProtoMCpr.h" +#include "IPProtoMessages.h" +#include "linkcprextensionapi.h" + +#include +#include +#include + +#ifdef SYMBIAN_ADAPTIVE_TCP_RECEIVE_WINDOW +#include +#endif //SYMBIAN_ADAPTIVE_TCP_RECEIVE_WINDOW + +#ifdef _DEBUG + #include +#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) + + 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) +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, 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 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) + + NODEACTIVITY_ENTRY(KNoTag, CoreNetStates::TSendNoBearer, MeshMachine::TAwaitingMessageState, TErrorTagOr >) + + //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(static_cast(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(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 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( + 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(aOption.Ptr())); + break; + + case KTestSoDummyNifSetLastSocketClosedTimeout: + iTickThreshold[ETimerMedium] = *(reinterpret_cast(aOption.Ptr())); + break; + + case KTestSoDummyNifSetLastSocketActivityTimeout: + iTickThreshold[ETimerLong] = *(reinterpret_cast(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(aOption.Ptr()); + if(disable) + { + DisableTimers(); + } + else + { + EnableTimers(); + } + break; + } + case KConnGetInterfaceName: + { + if(aOption.Length() != sizeof(TConnInterfaceName)) + { + return KErrArgument; + } + + TConnInterfaceName* connItfName; + connItfName = reinterpret_cast(const_cast(aOption.Ptr())); + + XInterfaceNames* itfNames = static_cast(const_cast(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(); + } +