--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/cbs/CbsServer/ServerSrc/Ccbsreccollector.cpp Tue Feb 02 01:11:09 2010 +0200
@@ -0,0 +1,584 @@
+/*
+* Copyright (c) 2003 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: This module contains the implementation of CCbsRecDecoder class
+* member functions.
+*
+* This class represents a collector, which stores pages of multipaged
+* messages. Complete messages are assembled and returned back to
+* the caller of CollectL() method.
+*
+* CCbsRecCollector stores message pages in a number of dynamic arrays.
+* If all but one page of a message are present in collector, and
+* the remaining page is received, the pages will be assembled and
+* the corresponding message chain deleted.
+*
+* The maximum number of incomplete messages stored in collector at once
+* is fixed and determined by KMaxCollectorMessages in CCbsRecCollector.cpp.
+*
+* CCbsRecCollector implements a circular list to contain message.
+* Each incomplete message occupies a slot in this list. If the list already
+* contains KMaxCollectorMessages messages, the next received multipaged
+* message will delete all received pages of the oldest message in list.
+*
+* On receival of a message page, the collector compares network information
+* (PLMN, LAC, CellId) of both messages to decide whether pages are of
+* the same message. In short, for pages to be of the same message
+* their network information have to meet the requirements set by the
+* geographical scope of the already collected page.
+* See ETSI GSM 03.41 for a detailed description.
+*
+*/
+
+
+// INCLUDE FILES
+#include "CbsServerPanic.h"
+#include "CCbsRecCollector.h"
+#include "CCbsRecMessage.h"
+#include "CCbsMessageFactory.h"
+#include "CCbsMessageCleanUpTimer.h"
+#include "CbsLogger.h"
+
+// CONSTANTS
+
+// Maximum number of pages in a single CB message.
+const TInt KMaxMessagePages = 15;
+
+// Maximum number of partial messages contained in the collector list.
+const TInt KMaxCollectorMessages = 10;
+
+// ================= MEMBER FUNCTIONS =======================
+
+// -----------------------------------------------------------------------------
+// CCbsRecCollector::CCbsRecCollector
+// C++ default constructor can NOT contain any code, that
+// might leave.
+// -----------------------------------------------------------------------------
+//
+CCbsRecCollector::CCbsRecCollector( CCbsMessageFactory& aFactory )
+ : iRootNodeIterator( 0 ),
+ iFactory( aFactory )
+ {
+ }
+
+// -----------------------------------------------------------------------------
+// CCbsRecCollector::ConstructL
+// Symbian 2nd phase constructor can leave.
+// -----------------------------------------------------------------------------
+//
+void CCbsRecCollector::ConstructL()
+ {
+ CBSLOGSTRING("CBSSERVER: >>> CCbsRecCollector::ConstructL()");
+
+ // initialize the root node array
+ iRootNodeArray = new ( ELeave ) CArrayPtrFlat< CMessageBuffer >
+ ( KMaxCollectorMessages );
+ iRootNodeArray->SetReserveL( KMaxCollectorMessages );
+ iMessageCleanupTimerArray = new ( ELeave ) CArrayPtrFlat< CCbsMessageCleanupTimer >
+ ( KMaxCollectorMessages );
+
+ for ( TInt i( 0 ); i < KMaxCollectorMessages; i++)
+ {
+ CMessageBuffer* array = new ( ELeave )
+ CMessageBuffer( KMaxMessagePages );
+ CleanupStack::PushL( array );
+ iRootNodeArray->AppendL( array );
+
+ CCbsMessageCleanupTimer* messageCleanUpTimer = CCbsMessageCleanupTimer::NewL( *this, *array );
+ iMessageCleanupTimerArray->AppendL( messageCleanUpTimer );
+
+ CleanupStack::Pop(); // array
+ }
+ CBSLOGSTRING("CBSSERVER: <<< CCbsRecCollector::ConstructL()");
+ }
+
+
+// -----------------------------------------------------------------------------
+// CCbsRecCollector::NewL
+// Two-phased constructor.
+// -----------------------------------------------------------------------------
+//
+ CCbsRecCollector* CCbsRecCollector::NewL( CCbsMessageFactory& aFactory )
+ {
+ CBSLOGSTRING("CBSSERVER: >>> CCbsRecCollector::NewL()");
+
+ CCbsRecCollector* self = new ( ELeave ) CCbsRecCollector( aFactory );
+ CleanupStack::PushL( self );
+ self->ConstructL();
+ CleanupStack::Pop();
+
+ CBSLOGSTRING("CBSSERVER: <<< CCbsRecCollector::NewL()");
+ return self;
+ }
+
+// Destructor
+CCbsRecCollector::~CCbsRecCollector()
+ {
+ CBSLOGSTRING("CBSSERVER: >>> CCbsRecCollector::~CCbsRecCollector()");
+
+ if ( iRootNodeArray )
+ {
+ TInt rootArrayLength( iRootNodeArray->Count() );
+ for ( TInt i( 0 ); i < rootArrayLength; i++ )
+ {
+ iRootNodeArray->At( i )->ResetAndDestroy();
+ }
+ iRootNodeArray->ResetAndDestroy();
+ delete iRootNodeArray;
+ }
+
+ if ( iMessageCleanupTimerArray )
+ {
+ iMessageCleanupTimerArray->ResetAndDestroy();
+ delete iMessageCleanupTimerArray;
+ }
+
+ CBSLOGSTRING("CBSSERVER: <<< CCbsRecCollector::~CCbsRecCollector()");
+ }
+
+// -----------------------------------------------------------------------------
+// CCbsRecCollector::CollectL
+// Adds a message to the location pointed by iRootNodeIterator.
+//
+// Checks if all pages of message are present. If all pages
+// are present, sets aCompleted to ECbsMessageComplete,
+// combines message pages into a single message, destroys
+// pages from the list and returns the complete message
+// to caller in aMessage.
+
+// Algorithm:
+// 1. Check if other pages of this message exist in list.
+// (serial number and message identifier match)
+// 2. True: Check if the other messages in chain need
+// to be deleted. This is based on geographical scope
+// and network information.
+// Add this message to the correct message chain.
+// False: Add this message to the chain pointed
+// by the iRootNodeIterator.
+// 3. Seek out the position in the chosed message chain so that
+// the page number sequence remains ordered (1, 2, .., n)
+// 4. Add the page to the correct position in chain.
+// 5. Check if all pages of this message exist (number count).
+// 6. True: Combine message pages into a single message
+// and return this page to the caller. Set aCompleted
+// to ECbsMessageComplete.
+// False: Set aCompleted to ECbsMessageIncomplete.
+//
+// Note: Ownership of aMessage assumed. aMessage assumed to be
+// on cleanup stack.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+CCbsMessage* CCbsRecCollector::CollectL(
+ CCbsMessage* aMessage, TInt aMessageType )
+ {
+ CBSLOGSTRING("CBSSERVER: >>> CCbsRecCollector::CollectL()");
+
+ // 1.
+ CMessageBuffer* array = FindChainContainingPage( *aMessage );
+ CCbsMessage* mergedMessage = NULL;
+
+ if ( array )
+ {
+ CBSLOGSTRING("CBSSERVER: CCbsRecCollector::CollectL(): array != NULL");
+
+ // 2. (True), 3.
+ __ASSERT_DEBUG( array->Count() > 0,
+ CbsServerPanic( ECbsCollectorArrayEmpty ) );
+
+ // Check geographical scope and network info to decide whether
+ // existing pages in this chain should be deleted.
+ TBool preserveExistingPages(
+ CheckPageAreaInfoMatch( *aMessage, *array->At( 0 ) ) );
+
+ CBSLOGSTRING2("CBSSERVER: CCbsRecCollector::CollectL(): preserveExistingPages: %d", preserveExistingPages );
+
+ if ( preserveExistingPages )
+ {
+ CBSLOGSTRING("CBSSERVER: CCbsRecCollector::CollectL(): Adding msg to chain...");
+ // aMessage is deleted, if it is a duplicate.
+ AddMessageToChainL( aMessage, *array );
+ CBSLOGSTRING("CBSSERVER: CCbsRecCollector::CollectL(): Adding msg to chain OK.");
+ }
+ else
+ {
+ CBSLOGSTRING("CBSSERVER: CCbsRecCollector::CollectL(): Deleting chain...");
+ DeleteChainL( *array );
+ CBSLOGSTRING("CBSSERVER: CCbsRecCollector::CollectL(): Deleting chain OK.");
+ array->InsertL( 0, aMessage );
+ CBSLOGSTRING("CBSSERVER: CCbsRecCollector::CollectL(): Msg added to array.");
+ }
+ // aMessage, ownership transferred to msg chain or
+ // aMessage has been deleted by AddMessageToChainL
+ CleanupStack::Pop();
+
+ // 5. Check if this chain contains all pages of the message.
+ if ( AllPagesPresent( *array ) )
+ {
+ CBSLOGSTRING("CBSSERVER: CCbsRecCollector::CollectL(): All pages present, merging...");
+
+ // 6. merge creates a new copy of this message
+ // leaves a pointer to msg to cleanup stack
+ mergedMessage = MergePagesLC( *array ); // on CS
+ CBSLOGSTRING("CBSSERVER: CCbsRecCollector::CollectL(): Merging OK.");
+
+ // we stop timer if its livecast message
+ if ( aMessageType == ECbsMessageLivecast )
+ {
+ CBSLOGSTRING("CBSSERVER: CCbsRecCollector::CollectL(): Msg type == ECbsMessageLivecast, stopping timer.");
+
+ TKeyArrayFix key(0, ECmpTUint16);
+ TInt index;
+ iRootNodeArray->Find( array, key, index);
+ iMessageCleanupTimerArray->At( index )->StopTimer();
+
+ CBSLOGSTRING("CBSSERVER: CCbsRecCollector::CollectL(): Timer stopped.");
+ }
+
+ CBSLOGSTRING("CBSSERVER: CCbsRecCollector::CollectL(): Deleting chain...");
+ DeleteChainL( *array );
+ CBSLOGSTRING("CBSSERVER: CCbsRecCollector::CollectL(): Deleting chain OK.");
+
+ CleanupStack::Pop(); // mergedMessage
+ }
+ }
+ else
+ {
+ CBSLOGSTRING("CBSSERVER: CCbsRecCollector::CollectL(): array == NULL");
+
+ // if message pagenumber does not start from 1, its not inserted to chain.
+ if ( aMessage->ThisPage () == 1 )
+ {
+ CBSLOGSTRING("CBSSERVER: CCbsRecCollector::CollectL(): aMessage->ThisPage () == 1");
+
+ // 2. (False)
+ // add this page as the first node in chain pointed by
+ // iRootNodeIterator. Delete any pages contained in the chain
+ // occuping this location first.
+ array = iRootNodeArray->At( iRootNodeIterator );
+ DeleteChainL( *array );
+ array->InsertL( 0, aMessage );
+ CleanupStack::Pop(); // aMessage, ownership transferred to msg chain.
+ iRootNodeIterator++;
+ // Return to the first message, if passed the
+ // maximum messages.
+ iRootNodeIterator %= KMaxCollectorMessages;
+
+ if ( aMessageType == ECbsMessageLivecast )
+ {
+ CBSLOGSTRING("CBSSERVER: CCbsRecCollector::CollectL(): aMessageType == ECbsMessageLivecast");
+
+ // start timeout timer for livecast message
+ TKeyArrayFix key(0, ECmpTUint16);
+ TInt index;
+ iRootNodeArray->Find( array, key, index);
+ iMessageCleanupTimerArray->At( index )->StartTimer();
+
+ CBSLOGSTRING("CBSSERVER: CCbsRecCollector::CollectL(): Timer started.");
+ }
+ }
+ else
+ {
+ CBSLOGSTRING("CBSSERVER: CCbsRecCollector::CollectL(): CleanupStack::Pop()");
+ CleanupStack::Pop();
+ }
+ }
+ // mergedMessage == NULL if msg not completed,
+ // otherwise return the complete message
+ CBSLOGSTRING("CBSSERVER: <<< CCbsRecCollector::CollectL(), returning mergedMessage.");
+
+ return mergedMessage;
+ }
+
+// -----------------------------------------------------------------------------
+// CCbsRecCollector::DeleteChainL
+// Deletes all message pages contained in aArray.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CCbsRecCollector::DeleteChainL(
+ CMessageBuffer& aArray ) const
+ {
+ aArray.ResetAndDestroy();
+ }
+
+// -----------------------------------------------------------------------------
+// CCbsRecCollector::AllPagesPresent
+// Returns ETrue if all pages of the message of aArray are present.
+// Counts pages in message chain aArray and compares the result
+// against the total number of pages in the message.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+TBool CCbsRecCollector::AllPagesPresent(
+ const CMessageBuffer& aArray ) const
+ {
+ TBool result( ETrue );
+ if ( TUint( aArray.Count() ) < aArray.At( 0 )->TotalPages() )
+ {
+ result = EFalse;
+ }
+
+ return result;
+ }
+
+// -----------------------------------------------------------------------------
+// CCbsRecCollector::MergePagesLC
+// Returns a complete message in aMessage.
+// Merges all pages in message chain aArray and returns
+// a pointer to the resulting assembled message. The pointer
+// is also left on the cleanup stack.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+CCbsMessage* CCbsRecCollector::MergePagesLC(
+ CMessageBuffer& aArray ) const
+ {
+ if ( aArray.Count() <= 0 )
+ {
+ User::Leave( KErrNotFound );
+ }
+ // Create a new message based on first message page in the chain.
+ CCbsMessage* message = iFactory.CreateMessageL( *aArray.At( 0 ) );
+
+ CleanupStack::PushL( message ); // left on cleanup stack
+
+ // Traverse through the chain and merge contents.
+ TInt length( 0 );
+ TInt count( aArray.Count() );
+
+ // If this is a Livecast message, use the 8-bit representation
+ // (message not decoded).
+ if ( message->IsLivecastMessage() )
+ {
+ for ( TInt j( 0 ); j < count; j++ )
+ {
+ length += aArray.At( j )->Contents8().Length();
+ }
+
+ __ASSERT_DEBUG( length >= 0, CbsServerPanic( ECbsCollectorMergeFailed ) );
+
+ message->ReserveContentSize8L( length );
+ count = aArray.Count();
+
+ for ( TInt i( 1 ); i < count; i++ )
+ {
+ message->AppendContent8( aArray.At( i )->Contents8() );
+ }
+ }
+ // Else use the 16-bit representation (message already decoded)
+ else
+ {
+ for ( TInt j( 0 ); j < count; j++ )
+ {
+ length += aArray.At( j )->Contents().Length();
+ }
+
+ __ASSERT_DEBUG( length >= 0, CbsServerPanic( ECbsCollectorMergeFailed ) );
+
+ message->ReserveContentSizeL( length );
+ count = aArray.Count();
+
+ // Append the rest of the pages (first page handled earlier)
+ for ( TInt i( 1 ); i < count; i++ )
+ {
+ message->AppendContent( aArray.At( i )->Contents() );
+ }
+ }
+ return message;
+ }
+
+// -----------------------------------------------------------------------------
+// CCbsRecCollector::FindChainContainingPage
+// Returns the buffer containing pages of same message.
+// Finds and returns a message chain which already contains pages
+// of aMessage's message. If none is found, NULL is returned.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+CMessageBuffer* CCbsRecCollector::FindChainContainingPage(
+ const CCbsMessage& aMessage ) const
+ {
+ TBool quitSeek( EFalse );
+ TInt seekIterator( 0 );
+ TCbsDbMessageKey key( aMessage.Key() );
+ TCbsDbTopicNumber topicNumber( aMessage.TopicNumber() );
+ CMessageBuffer* array = NULL;
+
+ // find out if the root array contains pages of this message
+ while ( ( seekIterator < KMaxCollectorMessages ) && !quitSeek )
+ {
+ array = iRootNodeArray->At( seekIterator );
+ if ( array->Count() > 0 )
+ {
+ CCbsMessage* msg = array->At( 0 );
+ if ( key == msg->Key() && topicNumber == msg->TopicNumber() )
+ {
+ quitSeek = ETrue;
+ }
+ }
+ seekIterator++;
+ }
+
+ if ( !quitSeek )
+ {
+ array = NULL;
+ }
+
+ return array;
+ }
+
+// -----------------------------------------------------------------------------
+// CCbsRecCollector::AddMessageToChainL
+// Adds message page aMessage to the correct position in message chain aArray
+//
+// Message chains are ordered in ascending page number order.
+// Duplicate pages are not accepted.
+//
+// Ownership of aMessage is transferred to aArray, if the given page
+// hasn't been already collected. The given page will be deleted,
+// if it already exists in the chain.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CCbsRecCollector::AddMessageToChainL(
+ CCbsMessage* aMessage,
+ CMessageBuffer& aArray ) const
+ {
+ // Find out a position for this page in the chain.
+ TInt chainLength( aArray.Count() );
+ TInt insertPosition( -1 );
+ TBool duplicate( EFalse );
+
+ for ( TInt i( 0 ); ( i < chainLength ) && !duplicate; i++ )
+ {
+ CCbsMessage* msg = aArray.At( i );
+ if ( insertPosition == -1 && msg->ThisPage() > aMessage->ThisPage() )
+ {
+ insertPosition = i;
+ }
+ else if ( msg->ThisPage() == aMessage->ThisPage() )
+ {
+ duplicate = ETrue; // This page has been already collected
+ delete aMessage;
+ }
+ }
+
+ // If this message was not a duplicate, add it to the chain
+ if ( !duplicate )
+ {
+ if ( insertPosition == -1 )
+ {
+ aArray.AppendL( aMessage );
+ }
+ else
+ {
+ aArray.InsertL( insertPosition, aMessage );
+ }
+ }
+ }
+
+// -----------------------------------------------------------------------------
+// CCbsRecCollector::CheckPageAreaInfoMatch
+// Checks if these pages can be merged. Returns ETrue, if merging is acceptable.
+//
+// Decision is based network information and geographical scope of
+// pages. Network information consists of cell id, location area code
+// and operator id.
+//
+// Assumption: aPage1 and aPage have identical message
+// identifiers and serial numbers.
+// Returns ETrue if the pages are of same message.
+// On EFalse previous pages should be deleted.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+TBool CCbsRecCollector::CheckPageAreaInfoMatch(
+ const CCbsMessage& aPage1,
+ const CCbsMessage& aPage2 ) const
+ {
+ RMobilePhone::TMobilePhoneNetworkInfoV1 info1;
+ RMobilePhone::TMobilePhoneNetworkInfoV1 info2;
+
+ aPage1.GetPLMN( info1 );
+ aPage2.GetPLMN( info2 );
+
+ TBool result( EFalse );
+
+ if ( info1.iCountryCode == KRecMessageNoNetworkInfo ||
+ info2.iCountryCode == KRecMessageNoNetworkInfo )
+ {
+ // Network info is unavailable; we must assume that the aPage1
+ // is of the same page area as aPage2, so we return ETrue
+ // unconditionally.
+ result = ETrue;
+ }
+
+ if ( info1.iCountryCode == info2.iCountryCode &&
+ info1.iNetworkId == info2.iNetworkId )
+ // PLMN match
+ {
+ if ( aPage1.LAC() == aPage2.LAC() )
+ // LAC match
+ {
+ if ( aPage1.CellId() == aPage2.CellId() )
+ // Cell match
+ {
+ // Full network information match
+ result = ETrue;
+ }
+ else
+ {
+ // Cell mismatch
+ if ( aPage1.GeographicalScope() ==
+ ECbsRecGeographicalScopeCell )
+ // Cell id mismatch and scoped cell wide.
+ {
+ result = EFalse;
+ }
+ else
+ // Cell id mismatch, not scoped cell wide.
+ {
+ result = ETrue;
+ }
+ }
+ }
+ else
+ // LAC mismatch
+ {
+ if ( aPage1.GeographicalScope() ==
+ ECbsRecGeographicalScopePLMN )
+ {
+ // LAC mismatch but scoped operator-wide
+ result = ETrue;
+ }
+ else
+ {
+ // LAC mismatch and not scoped operator-wide
+ result = EFalse;
+ }
+ }
+ }
+ else
+ // PLMN mismatch
+ {
+ result = EFalse; // operator mismatch
+ }
+
+ return result;
+ }
+
+
+// ================= OTHER EXPORTED FUNCTIONS ==============
+
+// End of File