telephonyprotocols/gprsumtsqosprt/src/context.cpp
changeset 0 3553901f7fa8
child 24 6638e7f4bd8f
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/telephonyprotocols/gprsumtsqosprt/src/context.cpp	Tue Feb 02 01:41:59 2010 +0200
@@ -0,0 +1,845 @@
+// Copyright (c) 2007-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:
+//
+
+#include <in_iface.h>
+
+#include "guqos.h"
+#include "iface.h"
+#include "tc.h"
+#include "parameters.h"
+#include "guqos_err.h"
+#include "async_request.h"
+#include "context.h"
+#include "guqos_log.h"
+
+#ifdef _LOG
+// *****************
+// Logging utilities
+// *****************
+
+// Return count of contiguous most significant 1-bits in TUint32
+static TInt MaskLength(TUint32 aAddr)
+	{
+	TInt count = 0;
+	// obviously, this is "brute force" counting
+	while (aAddr & 0x80000000)
+		{
+		count++;
+		aAddr <<= 1;
+		}
+	return count;
+	}
+
+//Return count of contiguous most significant 1-bits in IPv6 address.
+static TInt MaskLength(const TIp6Addr &aAddr)
+	{
+	TInt count = 0;
+	for (TUint i = 0; i < sizeof(aAddr.u.iAddr8) / sizeof(aAddr.u.iAddr8[0]); ++i)
+		if (aAddr.u.iAddr8[i] == 0xFF)
+			count += 8;
+		else
+			{
+			count += MaskLength(aAddr.u.iAddr8[i] << 24);
+			break;
+			}
+	return count;
+	}
+
+// A buffer to contain textual format of internet address
+class TAddressBuf : public TBuf<70>
+	{
+public:
+	TAddressBuf(const TIp6Addr& aAddr, TInt aMask, TUint16 aPort = 0);
+	};
+
+TAddressBuf::TAddressBuf(const TIp6Addr& aAddr, TInt aMask, TUint16 aPort)
+	{
+	TInetAddr addr(aAddr, 0);
+	addr.Output(*this);
+	if (aMask != 128)
+		{
+		if (aAddr.IsV4Mapped())
+			aMask -= 96;
+		_LIT(KFormat1, "/%u");
+		AppendFormat(KFormat1, aMask);
+		}
+	if (aPort)
+		{
+		_LIT(KFormat2, "#%u");
+		AppendFormat(KFormat2, (TInt)aPort);
+		}
+	}
+
+// Log packet filter address selector
+// Make this not "static", so that it can be used by other modules too...
+void LogPacketFilter(const TPacketFilter& aFilter)
+	{
+	TIp6Addr srcAddr;
+	Mem::Copy(&srcAddr,aFilter.iSrcAddr, sizeof(srcAddr));
+	TIp6Addr mask;
+	Mem::Copy(&mask,aFilter.iSrcAddrSubnetMask,sizeof(mask));
+	TAddressBuf addr(
+		srcAddr, 
+		MaskLength(mask),
+		aFilter.iSrcPortMin);
+	Log::Printf(_L("\t\t%u(%u) %S-%u dst#%u-%u SPI=%u TOS=%u FL=%u"),
+		aFilter.iId, aFilter.iEvaluationPrecedenceIndex,
+		&addr, (TInt)aFilter.iSrcPortMax,
+		(TInt)aFilter.iDestPortMin, (TInt)aFilter.iDestPortMax,
+		(TInt)aFilter.iIPSecSPI,
+		(TInt)aFilter.iTOSorTrafficClass,
+		(TInt)aFilter.iFlowLabel);
+	}
+#endif
+
+//	XPdpContextTimeoutLinkage
+//	************************
+//	Glue to bind timeout callback from the timeout manager into Timeout() call
+//	on the CPdpContext
+//
+//	*NOTE*
+//		This kludgery is all static and compile time, and only used in the constructor
+//		of CPdpContext.
+//
+
+// This ungainly manoevure is forced on us because the offset is not evaluated early enough by GCC3.4 to be
+// passed as a template parameter
+#if defined(__X86GCC__) || defined(__GCCE__)
+#define KPdpContextTimeoutOffset 716
+__ASSERT_COMPILE(KPdpContextTimeoutOffset == _FOFF(CPdpContext, iTimeout));
+#else
+#define KPdpContextTimeoutOffset _FOFF(CPdpContext, iTimeout)
+#endif
+
+class XPdpContextTimeoutLinkage : public TimeoutLinkage<CPdpContext, KPdpContextTimeoutOffset>
+	{
+public:
+	static void Timeout(RTimeout &aLink, const TTime & /*aNow*/, TAny * /*aPtr*/)
+		{
+		Object(aLink)->Timeout();
+		}
+	};
+
+
+CPdpContext* CPdpContext::NewL(CNif& aNifItem, const TContextType& aType, TUint8 aContextId)
+	{
+	CPdpContext* context = new (ELeave) CPdpContext(aNifItem, aType, aContextId);
+	CleanupStack::PushL(context);
+	context->ConstructL();
+	CleanupStack::Pop();
+	return context;
+	}
+
+CPdpContext::CPdpContext(CNif& aNifItem, TContextType aType, TUint8 aContextId) : 
+	iContextId(aContextId),
+	iNifItem(aNifItem),
+	iTimeout(XPdpContextTimeoutLinkage::Timeout)
+	{
+	iContextType = aType;
+	iContextStatus = RPacketContext::EStatusUnknown;
+	iFlows.SetOffset(_FOFF(CFlowData, iLink));
+	//iTrafficClass = -1;
+	}
+
+
+void CPdpContext::ConstructL()
+	{
+	}
+
+CPdpContext::~CPdpContext()
+	{
+	LOG(Log::Printf(_L("CPdpContext::~CPdpContext [ContextId=%d]"),iContextId));
+	while (!iFlows.IsEmpty())
+		{
+		// Any flows still attached to the PDP Context instance when
+		// we are in destructor, are deleted. If automatic move of
+		// flows to another context is the desired, it must be taken
+		// care before deleting the context (this destructor is also
+		// called from CNif destructor, and we cannot reference
+		// any other contexts on the CNif)
+		CFlowData* flow = iFlows.First();
+		// Deleting flow removes it from the list,
+		// and eventually the iFlows will become EMPTY!
+		delete flow;
+		}
+	iNifItem.CancelPendingRequest(this);
+	iTimeout.Cancel();	// ..if any active
+	}
+
+
+void CPdpContext::Timeout()
+	{
+	LOG(Log::Printf(_L("")));
+	if (Flows().IsEmpty())
+		{
+		LOG(Log::Printf(_L("CPdpContext::Timeout -- no flows, delete context")));
+		Delete();
+		Nif().DeletePdpContext(this);
+		}
+	else
+		{
+		LOG(Log::Printf(_L("CPdpContext::Timeout -- flows have re-appeared, keep context")));
+		// Some flows have come back, context is not
+		// deleted after all. But, because TFT's are
+		// not updated when the last flow leaves the
+		// context and this callback is scheduled, we
+		// now may have garbage TFT's left on the
+		// context. To cleanup the situation, schedule
+		// a CClose request.
+		CClose* request = CClose::New(*this);
+		if (request)
+			Nif().AddRequest(*request);
+		// If allocating request object fails, just
+		// ignore the issue of extra TFT's. It will
+		// eventually fix itself next time some flow
+		// leaves the context or context is deleted.
+		}
+	}
+	
+void CPdpContext::NotifyBlockingState(TUint aOldBlocking)
+	{
+	const TUint blocked = IsFlowBlocked();
+	if (blocked == aOldBlocking)
+		{
+		LOG(Log::Printf(_L("\t\tContextId = %d (channel=%d) Blocking unchanged (%d)"), ContextId(), ChannelId(), blocked));
+		return;		// No Change, no need to notify anyone.
+		}
+	LOG(Log::Printf(_L("\t\tContextId = %d (channel=%d) Blocking changed to (%d)"), ContextId(), ChannelId(), blocked));
+
+	MEventInterface *notifier = Nif().Module().QoSEvent();
+	if (!notifier)
+		{
+		LOG(Log::Printf(_L("\tOops -- QoS has not setup the MEventInterface")));
+		return;
+		}
+	
+	// Set all flows associated with this context into blocked or unblocked state
+	TDblFlowIter iter(iFlows);
+	CFlowData *flow;
+	//?? Potentially dangerout iterator, becuase BlockFlow upcall
+	//?? may call back and modify the list!
+	while ((flow = iter++) != NULL)
+		{
+		// Throw away "const". The MEventInterface is misdeclared to
+		// use non-const reference parameter, when const reference
+		// would be the correct way...
+		if (blocked)
+			notifier->BlockFlow((CFlowContext &)flow->FlowContext());
+		else
+			notifier->UnBlockFlow((CFlowContext &)flow->FlowContext());
+		}
+	}
+
+// Block flows on this context, no packets are sent to these flows after this.
+void CPdpContext::Block()
+	{
+	const TUint blocked = IsFlowBlocked(); // get old state
+	iBlocked = ETrue;
+	NotifyBlockingState(blocked);
+	}
+
+
+// Unblock flows on this context. Re-enable packet sending for these flows.
+void CPdpContext::UnBlock()
+	{
+	LOG(Log::Printf(_L("\tCPdpContext::UnBlock()")));
+	const TUint blocked = IsFlowBlocked(); // get old state
+	iBlocked = EFalse;
+	NotifyBlockingState(blocked);
+	}
+
+
+// Check if CFlowContext matches with packet filter attributes
+TBool CPdpContext::MatchPacketFilter(const TPacketFilter& aFlow, const TPacketFilter& aFilter) const
+	{
+	if (Mem::Compare(aFlow.iSrcAddr, 16, aFilter.iSrcAddr, 16) != 0)
+		return EFalse;
+	
+	if (Mem::Compare(aFlow.iSrcAddrSubnetMask, 16, aFilter.iSrcAddrSubnetMask, 16) != 0)
+		return EFalse;
+
+	if (aFlow.iProtocolNumberOrNextHeader != (TUint)aFilter.iProtocolNumberOrNextHeader)
+		return EFalse;
+
+	if (aFlow.iDestPortMin < aFilter.iDestPortMin || aFlow.iDestPortMax > aFilter.iDestPortMax)
+		return EFalse;
+
+	if (aFlow.iSrcPortMin < aFilter.iSrcPortMin || aFlow.iSrcPortMax > aFilter.iSrcPortMax)
+		return EFalse;
+
+	if (aFlow.iFlowLabel != aFilter.iFlowLabel)
+		return EFalse;
+
+	if (aFlow.iTOSorTrafficClass != aFilter.iTOSorTrafficClass)
+		return EFalse;
+
+	return ETrue;
+	}
+
+
+// Return QoS ranking according to TS 23.107
+//
+// (only used for turning some secondary context into new default/primary context)
+TInt CPdpContext::GetQoSRanking()
+	{
+	TInt ranking = 6;
+	switch (TrafficClass())
+		{
+	case RPacketQoS::ETrafficClassConversational:
+		ranking = 2;
+		break;
+
+	case RPacketQoS::ETrafficClassStreaming:
+		ranking = 3;
+		break;
+
+	case RPacketQoS::ETrafficClassInteractive:
+		{
+		const TInt priority = iNegotiated.iTrafficHandlingPriority;
+		if (priority == 1)
+			ranking = 1;
+		else if (priority == 2)
+			ranking = 4;
+		else
+			ranking = 5;
+		}
+		break;
+
+	default:
+	case RPacketQoS::ETrafficClassBackground:
+		ranking = 6;
+		break;
+		}
+	return ranking;
+	}
+
+
+TInt CPdpContext::FindPacketFilterId()
+	{
+	// Valid filter id's are from 1 - 8, find one which
+	// is not yet in use: First find out used id's into mask
+	
+	// Note: 3GPP specification uses 0-7 as the filter id.
+	// The etelpckt.h range [1-8] is adjusted to [0-7] at
+	// lower layers.
+	TUint mask = 0;
+	for (TInt i = iNumFilters; --i >= 0;)
+		{
+		const TPacketFilter& f = iFilters[i];
+		// Note silly naked constants, but there are no symbols for these?
+		if (f.iId > 0 && f.iId < 9)
+			{
+			mask |= (1 << f.iId);
+			}
+		}
+	// Find an empty id slot.
+	for (TInt j = 0; ++j < 9; )
+		{
+		if ((mask & (1 << j)) == 0)
+			return j;
+		}
+	//??What error is supposed to be returned?
+	//return KErrNotFound;
+	return KErrOverflow;
+	}
+
+
+// Add packet filter to TFT, return error or number of filters added
+TInt CPdpContext::AddPacketFilter(CFlowData& aFlow, TTFTInfo& aTft)
+	{
+	TInt ret = KErrNone;
+
+	LOG(Log::Printf(_L("\t\tCPdpContext::AddPacketFilter")));
+
+
+	TPacketFilter& flow_filter = aFlow.PacketFilter();
+
+	for (TInt i = iNumFilters; --i >= 0; )
+		if (MatchPacketFilter(flow_filter, iFilters[i]))
+			{
+			// The flow is already covered by an existing filter,
+			// no need to add anything, return 0.
+			return 0;
+			}
+
+	// Update flow filter with dynamic fields.
+	// This only marks the Id and evaluation precedende as reserved. It
+	// does not add the filter to the context object (iNumFilters is not
+	// incremented).
+	// It is assumed that once NIF completes the request, it will return
+	// the complete state of the context, including all currently active
+	// filters. At that point GUQOS state is recomputed to match the
+	// actual reality.
+	
+	// Not touching the iNumFilters works for now, because there is at
+	// most one negotiation open on any interface, and because negoatiation
+	// currently adds at most one new filter.
+	// If above changes, and some negotiation would need to add multiple
+	// filters, then additional logic needs to remember the to-be-added
+	// filters and assigned ids.
+	ret = FindPacketFilterId();
+	if (ret < 0)
+		{
+		// All filter slots already used!
+		LOG(Log::Printf(_L("\t\tNo PacketFilterId -- all used")));
+		return ret;
+		}
+	flow_filter.iId = ret;
+	LOG(Log::Printf(_L("\t\tPacketFilterId: %d"), flow_filter.iId));
+
+	flow_filter.iEvaluationPrecedenceIndex = Nif().FindEvaluationPrecedence();
+	LOG(Log::Printf(_L("\t\tEvaluationPrecedenceIndex: %d"), flow_filter.iEvaluationPrecedenceIndex));
+	LOG(LogPacketFilter(flow_filter));
+	ret = aTft.AddPacketFilter(flow_filter);
+	return ret < 0 ? ret : 1;	// if OK, 1 filter added
+	}
+
+
+// Remove packet filter(s) from the TFT, return error or number of filters to remove
+TInt CPdpContext::RemovePacketFilter(TTFTInfo& aTft, const CFlowData* aFlow)
+	/**
+	* Find the set of unused filters.
+	*
+	* Scan all currently attached flows and match them against current set of
+	* filters. Return the list of filters that didn't match any flows.
+	*
+	* One optional flow can be treated as non-attached (even if it appears
+	* in the attached list) and excluded matching test. This allows caller
+	* to leave a flow in the attached list for later removal.
+	*
+	* @retval aTft The list of unused filters
+	* @par	aFlow Optional flow to exclude from the matching.
+	*
+	* @return Negative error code (< 0), or number of unsed filters (>= 0),
+	* which were added into.
+	*/
+	{
+	// Because filters can match multiple flows (if someone actually starts
+	// using the netmask and port ranges), this must test whether any of the
+	// current filters become unnecessary when the indicated flow has been
+	// removed.
+
+	LOG(Log::Printf(_L("\t\tCPdpContext::RemovePacketFilter")));
+	TUint mask = 0;
+
+	// If the context is current designated "primary", then all filters
+	// must be removed (there can be filters only if this primary is
+	// a secondary context that has been assigned as new default).
+	if (!IsPrimary())
+		{
+		TDblFlowIter iter(iFlows);
+		CFlowData *flow;
+		while ((flow = iter++) != NULL)
+			{
+			if (flow == aFlow)
+				continue;	// skip the flow being removed.
+			const TPacketFilter& flow_filter = flow->PacketFilter();
+			for (TInt i = iNumFilters; --i >= 0; )
+				if (MatchPacketFilter(flow_filter, iFilters[i]))
+					{
+					mask |= (1 << i);	// Mark this filter as used.
+					break;				// No need to test other filters for this flow.
+					}
+			}
+		}
+	// Schedule removal of all filters that were not used.
+	TInt count = 0;
+	for (TInt j = 0; j < iNumFilters; ++j, mask >>= 1)
+		{
+		if ((mask & 1) == 0)
+			{
+			// This filter is not needed anymore
+			LOG(LogPacketFilter(iFilters[j]));
+			TInt ret = aTft.AddPacketFilter(iFilters[j]);
+			if (ret != KErrNone)
+				return ret;
+			++count;
+			}
+		}
+	return count;
+	}
+
+//
+// Set QoS parameters for a Pdp context.
+// Note: Change is not signaled to network until Activate/ModifyActive is called.
+//
+TInt CPdpContext::SetQoS(const TQoSParameters& aGeneric, const TQoSRequested& aUmts)
+	{
+	LOG(Log::Printf(_L("\t\tCPdpContext::SetQoSL on PDP Context [ContextId=%d]"),iContextId));
+
+	TContextParameters& parameters(Nif().ContextParameters());
+	
+	TQoSRequested requested;
+	ParameterMapper::MapGenericToRel99(aGeneric, requested);
+
+	// If adapt flag set, minimum params are set to unspecified
+	if (aGeneric.AdaptMode())
+		requested.ClearMinimumParameters();
+
+	ParameterMapper::CombineOverride(aUmts, requested);
+
+	parameters.iContextConfig.SetUMTSQoSReq(requested);
+	parameters.iContextConfig.SetPdpCompression(requested.iHeaderCompression);
+	parameters.iContextInfo.iContextId = iContextId;
+	parameters.iContextType = iContextType;
+	TPckg<TContextParameters> options(parameters);
+	const TInt ret = Nif().Interface().Control(KSOLInterface, KContextQoSSet, options);
+	return ret == KErrNone ? parameters.iReasonCode : ret;
+	}
+
+
+// Set QoS parameters for a Pdp context from defaults
+// Note: Change is not signaled to network until Activate/ModifyActive is called.
+TInt CPdpContext::SetQoS()
+	{
+	LOG(Log::Printf(_L("\t\tCPdpContext::SetQoSL on PDP Context [ContextId=%d ChannelId=%d]"), iContextId, iChannelId));
+
+	TContextParameters& parameters(Nif().ContextParameters());
+	TQoSRequested policy;
+	Nif().Module().GetDefaultParameters(policy, Nif().IapId());
+	parameters.iContextConfig.SetUMTSQoSReq(policy);
+	parameters.iContextConfig.SetPdpCompression(policy.iHeaderCompression);
+	parameters.iContextInfo.iContextId = iContextId;
+	parameters.iContextType = iContextType;
+	TPckg<TContextParameters> options(parameters);
+	const TInt ret = Nif().Interface().Control(KSOLInterface, KContextQoSSet, options);
+	return ret == KErrNone ? parameters.iReasonCode : ret;
+	}
+
+
+// SetQoS response
+void CPdpContext::SetQoSReply(CRequestBase* aRequest, const TContextParameters& aParams)
+	{
+	LOG(Log::Printf(_L("CPdpContext::SetQoSReply [ContextId=%d] [ReasonCode=%d]"),iContextId,aParams.iReasonCode));
+
+	// Note: Because Run may complete the request, and resulting upcall
+	// to QoS may delete anything within the GUQOS, don't assume "this"
+	// exists after Run! Any actions on this must be done before the
+	// Run call.
+	if (aRequest)
+		aRequest->Run(EPendingSetQoS, this, aParams);
+	}
+
+TInt CPdpContext::ModifyTft(const TTFTOperationCode& aTFTOperationCode, const TTFTInfo& aTft)
+	{
+	LOG(Log::Printf(_L("\t\tModifyTFT on PDP Context [ContextId=%d]"),iContextId));
+
+	TPckg<TContextParameters> options(Nif().ContextParameters());
+	options().iContextConfig.SetTFTInfo(aTft);
+	options().iContextInfo.iContextId = iContextId;
+	options().iContextType = iContextType;
+	options().iTFTOperationCode = aTFTOperationCode;
+
+	const TInt ret = Nif().Interface().Control(KSOLInterface, KContextTFTModify, options);
+	return ret == KErrNone ? options().iReasonCode : ret;
+	}
+
+void CPdpContext::ModifyTftReply(CRequestBase* aRequest, const TContextParameters& aParams)
+	{
+	LOG(Log::Printf(_L("CPdpContext::ModifyTftReply [ContextId=%d] [ReasonCode=%d]"),iContextId,aParams.iReasonCode));
+	if (!aRequest)
+		return;
+
+	// Translate operation code into "request code"...
+	TRequest code = EPendingAny;;
+	switch(aParams.iTFTOperationCode)
+		{
+	case KAddFilters:
+		code = EPendingPacketFilterAdd;
+		break;
+
+	case KRemoveFilters:
+		code = EPendingPacketFilterRemove;
+		break;
+
+	case KDeleteTFT:
+		code = EPendingTFTRemove;
+		break;
+
+	case KAddSblpParameter:
+		code = EPendingSblpParameterAdd;
+		break;
+			
+	case KRemoveSblpParameter:
+		code = EPendingSblpParameterRemove;
+		break;
+
+	default:
+		// Should not happen, but if it does, let the request state
+		// machine handle the problem (code is now EPendingAny!)
+		break;
+		}
+	// Note: Because Run may complete the request, and resulting upcall
+	// to QoS may delete anything within the GUQOS, don't assume "this"
+	// exists after Run! Any actions on this must be done before the
+	// Run call.
+	aRequest->Run(code, this, aParams);
+	}
+
+
+// Activate context
+TInt CPdpContext::Activate()
+	{
+	LOG(Log::Printf(_L("\t\tCPdpContext::Activate PDP Context [ContextId=%d]"),iContextId));
+
+	TContextParameters& parameters(Nif().ContextParameters());
+
+	// Activate only needs the context id.
+	parameters.iContextInfo.iContextId = iContextId;
+	//?? Does it need the "context type"?
+	parameters.iContextType = iContextType;
+	TPckg<TContextParameters> options(parameters);
+	const TInt ret =  Nif().Interface().Control(KSOLInterface, KContextActivate, options);
+	return ret == KErrNone ? parameters.iReasonCode : ret;
+	}
+
+// Activate context response
+void CPdpContext::ActivateReply(CRequestBase* aRequest, const TContextParameters& aParams)
+	{
+	LOG(Log::Printf(_L("CPdpContext::ActivateReply() [ContextId=%d]"),iContextId));
+
+	Update(aParams);
+	// Must be done before Run (see below)!
+	if (ContextActive())
+		UnBlock();
+	// Note: Because Run may complete the request, and resulting upcall
+	// to QoS may delete anything within the GUQOS, don't assume "this"
+	// exists after Run! Any actions on this must be done before the
+	// Run call.
+	if (aRequest)
+		{
+		aRequest->Run(EPendingActivate, this, aParams);
+		}
+	}
+
+// Modify active Pdp context
+TInt CPdpContext::ModifyActive()
+	{
+	LOG(Log::Printf(_L("\t\tCPdpContext::ModifyActive [ContextId=%d]"),iContextId));
+
+	TContextParameters& parameters(Nif().ContextParameters());
+	parameters.iContextInfo.iContextId = iContextId;
+	TPckg<TContextParameters> options(parameters);
+
+	const TInt ret(Nif().Interface().Control(KSOLInterface, KContextModifyActive, options));
+	return ret == KErrNone ? parameters.iReasonCode : ret;
+	}
+
+void CPdpContext::ModifyActiveReply(CRequestBase* aRequest, const TContextParameters& aParams)
+	{
+	LOG(Log::Printf(_L("CPdpContext::ModifyActiveReply [ContextId=%d] [ReasonCode=%d]"),iContextId,aParams.iReasonCode));
+	Update(aParams);
+
+	// Must be done before Run (see below)!
+	if (ContextActive())
+		UnBlock();
+	// Note: Because Run may complete the request, and resulting upcall
+	// to QoS may delete anything within the GUQOS, don't assume "this"
+	// exists after Run! Any actions on this must be done before the
+	// Run call.
+	if (aRequest)
+		aRequest->Run(EPendingModifyActive, this, aParams);
+	}
+
+// QoS parameters have changed
+void CPdpContext::ParametersChangedEvent(const TContextParameters& aParams)
+	{
+	LOG(Log::Printf(_L("CPdpContext::ParametersChangedEvent [ContextId=%d] [ReasonCode=%d]"),iContextId,aParams.iReasonCode));
+	Update(aParams);
+	// Notify applications about QoS change
+	GenerateEvent(KPfqosEventAdapt, aParams);
+	}
+
+void CPdpContext::DeleteEvent(const TContextParameters& aParams)
+	{
+	LOG(Log::Printf(_L("CPdpContext::DeleteEvent [ContextId=%d] [ReasonCode=%d]"),iContextId,aParams.iReasonCode));
+	GenerateEvent(KPfqosEventFailure, aParams);
+	}
+
+// delete Pdp context
+TInt CPdpContext::Delete()
+	{
+	if (!IsPrimary())
+		{
+		LOG(Log::Printf(_L("\t\tCPdpContext::Delete [ContextId=%d]"),iContextId));
+
+		TContextParameters& parameters(Nif().ContextParameters());
+		parameters.iContextInfo.iContextId = iContextId;
+		TPckg<TContextParameters> options(parameters);
+		Nif().Interface().Control(KSOLInterface, KContextDelete, options);
+		}
+	return KErrNone;
+	}
+
+
+void CPdpContext::FlowAttached()
+	{
+	++iRefs;	
+	}
+
+void CPdpContext::FlowDetached()
+	{
+	--iRefs;
+	if (!IsPrimary() && iFlows.IsEmpty())
+		{
+		ASSERT(iRefs == 0);
+		iTimeout.Set(Nif().Module().TimeoutManager(), Nif().Module().Options().iTimeout);
+		}
+	}
+
+void CPdpContext::GenerateEvent(TUint aEventType, const TContextParameters& aParams)
+	{
+	// Generate event for channels
+	MEventInterface *notifier = Nif().Module().QoSEvent();
+	if (!notifier)
+		return;	// Nobody to notify!
+
+	RExtensionData extension;
+	TInt ret = extension.CreateExtension(aParams.iContextConfig, aParams.iReasonCode);
+	// if cannot generate full return information, try with plain reason code
+	if (ret != KErrNone)
+		(void)extension.SetErrorCode(aParams.iReasonCode);
+	
+	if (ChannelId())
+		{
+		// The PDP context represents a channel, one event for all joined flows
+		notifier->NotifyEvent(ChannelId(), aEventType, NULL, extension);
+		}
+	else
+		{
+		// Generate event for each flow
+		TDblFlowIter iter(iFlows);
+		CFlowData *flow;
+		while ((flow = iter++) != NULL)
+			{
+			// Throw away "const". The MEventInterface is misdeclared to
+			// use non-const reference parameter, when const reference
+			// would be the correct way...
+			notifier->NotifyEvent((CFlowContext &)flow->FlowContext(), aEventType, NULL, extension);
+			}
+		}
+	extension.Close();
+	}
+
+
+void CPdpContext::SetContextStatus(const RPacketContext::TContextStatus& aStatus)
+	{
+#ifdef _LOG
+	TBuf<40> statusBefore;
+
+	switch(iContextStatus)
+		{
+		case RPacketContext::EStatusUnknown:		statusBefore.Append(_L("EStatusUnknown"));		break;
+		case RPacketContext::EStatusInactive:		statusBefore.Append(_L("EStatusInactive"));		break;
+		case RPacketContext::EStatusActivating:		statusBefore.Append(_L("EStatusActivating"));	break;
+		case RPacketContext::EStatusActive:			statusBefore.Append(_L("EStatusActive"));		break;
+		case RPacketContext::EStatusDeactivating:	statusBefore.Append(_L("EStatusDeactivating"));	break;
+		case RPacketContext::EStatusSuspended:		statusBefore.Append(_L("EStatusSuspended"));	break;
+		case RPacketContext::EStatusDeleted:		statusBefore.Append(_L("EStatusDeleted"));		break;
+		default:									statusBefore.Append(_L("error"));				break;
+		}
+
+	TBuf<40> statusAfter;
+
+	switch(aStatus)
+		{
+		case RPacketContext::EStatusUnknown:		statusAfter.Append(_L("EStatusUnknown"));		break;
+		case RPacketContext::EStatusInactive:		statusAfter.Append(_L("EStatusInactive"));		break;
+		case RPacketContext::EStatusActivating:		statusAfter.Append(_L("EStatusActivating"));	break;
+		case RPacketContext::EStatusActive:			statusAfter.Append(_L("EStatusActive"));		break;
+		case RPacketContext::EStatusDeactivating:	statusAfter.Append(_L("EStatusDeactivating"));	break;
+		case RPacketContext::EStatusSuspended:		statusAfter.Append(_L("EStatusSuspended"));		break;
+		case RPacketContext::EStatusDeleted:		statusAfter.Append(_L("EStatusDeleted"));		break;
+		default:									statusAfter.Append(_L("error"));				break;
+		}
+
+	LOG(Log::Printf(_L("\tCPdpContext::SetContextStatus, iContextStatus before: {%S}, after: {%S}"), &statusBefore, &statusAfter));
+#endif
+
+	const TUint blocked = IsFlowBlocked();
+	iContextStatus = aStatus;
+	NotifyBlockingState(blocked);
+	}
+
+void CPdpContext::Update(const TContextParameters& aParams)
+	{
+	LOG(Log::Printf(_L("\tUpdate Context id = %d (channel=%d)"), ContextId(), ChannelId()));
+	// Save some basic negotiated values to the context instance.
+	aParams.iContextConfig.GetUMTSQoSNeg(iNegotiated);
+
+	// *NOTE* *ASSUMED*
+	// This function is called on completion of MofifyActive/Activate and
+	// in some other cases. Assume that the TContextParameters contains the
+	// current state of the context as seen by lower layers. The iReasonCode
+	// only indicates that the indicated operation failed or succeeded.
+	// The current state is provided even if iReasonCode != KErrNone.
+
+	LOG(Log::Printf(_L("\tCurrent TFT from NIF to PDP ContextId=%d"), ContextId()));
+	//?? The specification of TTFTInfo class is plain idiotic. There is no way to
+	//?? scan through filters with constant object. There is no way to get a
+	//?? const ref to filter inside TFTInfo, instead all scanning always requires
+	//?? inconvenient copying of the element (in addition to the fact that,
+	//?? you often have to copy the whole TTFTInfo structure to local store
+	//?? for a "modifiable copy" (which really should not be needed!)
+	
+	// Avoid some of the problems by *not* keeping TTFTInfo in CPdpContext, and
+	// maintain own simple array of TPacketFilters instead, which can at least
+	// be looked without needing to copy the elements each time...
+	TTFTInfo& tft = ((RContextConfig&)aParams.iContextConfig).TFTInfo();
+	tft.SetToFirst();
+	iNumFilters = 0;
+	while (iNumFilters < KMaxPacketFilters && tft.NextPacketFilter(iFilters[iNumFilters]) == KErrNone)
+		{
+		LOG(LogPacketFilter(iFilters[iNumFilters]));
+		++iNumFilters;
+		}
+	Nif().RecomputeEvaluationPrecedences();
+
+#ifdef _LOG
+	RPacketContext::CTFTMediaAuthorizationV3 *sblpParams;
+	TRAPD(err, sblpParams = RPacketContext::CTFTMediaAuthorizationV3::NewL());
+	if (err != KErrNone)
+		return;	// Blah!!
+	if (tft.GetSblpToken(*sblpParams) == KErrNone)
+		{
+		TBuf<KMaxFQDNLength> mat;
+		mat.Copy(sblpParams->iAuthorizationToken);
+		//coverity[leave_without_push]
+		Log::Printf(_L("\t\tMedia authorization: '%S'"),&mat);
+		for( TInt i = 0; i < sblpParams->iFlowIds.Count(); i++ )
+			{
+			//coverity[leave_without_push]
+			Log::Printf(_L("\t\tFlowId %d.  Media <%d> Flow <%d>"),
+				i,
+				sblpParams->iFlowIds[i].iMediaComponentNumber,
+				sblpParams->iFlowIds[i].iIPFlowNumber);
+			}
+		}
+	else
+		//coverity[leave_without_push]
+		Log::Printf(_L("\t\tNo SBLP"));
+	delete sblpParams;
+#endif
+	// Do the status change last, because it may generate events
+	// and callbacks. Now all the context state is updated...
+	SetContextStatus(aParams.iContextInfo.iStatus);
+	}
+
+// Rename: IsDefaultContext()
+TBool CPdpContext::IsPrimary() const
+	{
+	return (iNifItem.DefaultPdpContext() == this);
+	}
+