diff -r 6b1d113cdff3 -r 6638e7f4bd8f telephonyprotocols/gprsumtsqosprt/src/context.cpp --- a/telephonyprotocols/gprsumtsqosprt/src/context.cpp Mon May 03 13:37:20 2010 +0300 +++ b/telephonyprotocols/gprsumtsqosprt/src/context.cpp Thu May 06 15:10:38 2010 +0100 @@ -1,845 +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 - -#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 - { -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 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 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 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 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 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 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 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); - } - +// 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 + +#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 + { +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 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 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 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 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 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 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 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); + } +