diff -r 000000000000 -r 3553901f7fa8 smsprotocols/smsstack/smsprot/Src/smspreassemblystore.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/smsprotocols/smsstack/smsprot/Src/smspreassemblystore.cpp Tue Feb 02 01:41:59 2010 +0200 @@ -0,0 +1,690 @@ +// 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 "smsstacklog.h" +#include "gsmubuf.h" +#include "smspreassemblystore.h" + +/** + * Populate entry information from sms message. + * @param aEntry fills up re-assembly entry information from aSmsMessage. + * @param aSmsMessage refernce to sms message. + * @param aNumSmss number of sms. + */ +void CReassemblyStoreUtility::PopulateEntry(TSmsReassemblyEntry& aEntry,const CSmsMessage& aSmsMessage,TInt aNumSmss) + { + LOGSMSPROT1("CReassemblyStoreUtility::PopulateEntry"); + aEntry.SetReference(0); + aEntry.SetTotal(1); + aEntry.SetCount(1); + + if (aSmsMessage.TextPresent()) + { + if (aSmsMessage.SmsPDU().TextConcatenated()) + { + aEntry.SetReference(aSmsMessage.SmsPDU().ConcatenatedMessageReference()); + aEntry.SetTotal(aSmsMessage.SmsPDU().NumConcatenatedMessagePDUs()); + aEntry.SetCount(aSmsMessage.IsComplete()? aEntry.Total(): aNumSmss); + } + TInt bits7to4=aSmsMessage.SmsPDU().Bits7To4(); + TInt count=aSmsMessage.SmsPDU().UserData().NumInformationElements(); + TInt identifier1=0xFF; + TInt identifier2=0x00; + for (TInt i=0; iidentifier2) + identifier2=identifier; + } + } + + if ((bits7to4>=TSmsDataCodingScheme::ESmsDCSMessageWaitingIndicationDiscardMessage) && (bits7to4<=TSmsDataCodingScheme::ESmsDCSMessageWaitingIndicationUCS2)) + aEntry.SetBits7to4andIdentifiers(bits7to4, identifier1, identifier2); + else + aEntry.SetBits7to4andIdentifiers(0, identifier1, identifier2); + } + + //Set the logServerId to aSmsMessage.LogServerId() + + aEntry.SetLogServerId(aSmsMessage.LogServerId()); + + const CSmsPDU::TSmsPDUType type(aSmsMessage.Type()); + aEntry.SetPduType(type); + aEntry.SetPassedToClient(EFalse); + + aEntry.SetStorage(aSmsMessage.Storage()); + if ((type!=CSmsPDU::ESmsSubmitReport) && (type!=CSmsPDU::ESmsDeliverReport)) + { + // Strip out spaces etc from address + TGsmSmsTelNumber parsedaddress; + aSmsMessage.ParsedToFromAddress(parsedaddress); + aEntry.SetDescription2(parsedaddress.iTelNumber); + } + + aEntry.SetTime(aSmsMessage.Time()); + } + +/** + * Returns the private path of the component. + * @param aFs File Server handle. + * @param aPath (retrurns) private path of the component. + */ +void CReassemblyStoreUtility::PrivatePath(RFs& aFs, TDes& aPath) + { + LOGSMSPROT1("CReassemblyStoreUtility::PrivatePath()"); + + TDriveUnit driveUnit(KStoreDrive); + TDriveName drive=driveUnit.Name(); + aPath.Insert(0, drive); + //append private path + TPath privatePath; + aFs.PrivatePath(privatePath); + aPath.Append(privatePath); + aPath.Append(KStoreSubDir); + } // CReassemblyStoreUtility::PrivatePath + +/** + * Constructor. +*/ +CReassemblyStore::CReassemblyStore(RFs& aFs) : iFs(aFs), iEntryArray(KFlatArrayGranularity) + { + iLastReceivedTime.UniversalTime(); + iLastRealTime = iLastReceivedTime; + } + +/** + * Destructor. +*/ +CReassemblyStore::~CReassemblyStore() + { + iEntryArray.Reset(); + } + +/** +It cleans up the re-assembly store. +This function will be called to allow re-assembly store to initialize/clean-up + +@internalComponent +*/ +void CReassemblyStore::InitializeL() + { + LOGSMSPROT1("CClass0SmsReassemblyStore::InitializeL()"); + // Initialize Re-assembly store. + OpenStoreL(); + BeginTransactionLC(); + TInt count = iEntryArray.Count(); + while (count--) + { + TReassemblyEntry entry= iEntryArray[count]; + if ((entry.Storage() == CSmsMessage::ESmsSIMStorage) || (entry.Storage() == CSmsMessage::ESmsCombinedStorage)) + { + DeleteEntryL(entry); + } + else + { + SetPassedToClientL(entry, EFalse); + } + } + CommitTransactionL(); + Close(); + } + +/** + * Purges the reassembly file store. + * + * After a multipart message, it delete all the old entries. + * + * Entries will be purged when: 1) The complete message is received; 2) After + * aTimerintervalMinutes, if aPurgeIncompletely is false. + * + * PurgeL() will be called after the booting of the device or when a message + * has been received. + * + * This function opens and closes the file automatically. + * + * + * @param aTimeIntervalMinutes Purge time + * @param aPurgeIncompleteOnly Purge complete messages flag + */ +void CReassemblyStore::PurgeL(const TTimeIntervalMinutes& aTimeIntervalMinutes,TBool aPurgeIncompleteOnly) + { + //Call purging function + LOGSMSPROT3("CReassemblyStore::PurgeL(): aTimeIntervalMinutes=%d, aPurgeIncompleteOnly=%d", + aTimeIntervalMinutes.Int(), aPurgeIncompleteOnly); + + // TODO - flag + // we could also save the call of the method from the consruction of the smsprot + if( aPurgeIncompleteOnly ) + return; + + TInt count=iEntryArray.Count(); + LOGSMSPROT2("CClass0SmsReassemblyStore::PurgeL(): count=%d", count); + + TTime time; + time.UniversalTime(); + + // we open the file outside the loop + // to save some CPU + BeginTransactionLC(); + for (TInt i=count-1; i>=0; i--) + { + //TReassemblyEntry entry=iEntryArray[i]; + if (time > (iEntryArray[i].Time()+aTimeIntervalMinutes)) + // TODO - flag + // check the logic o the aPurgeIncompleteOnly flg + // don't purge the store if the entry is complete + // entry.IsComplete() ) + { + DeleteEntryL(iEntryArray[i]); + } + } + CommitTransactionL(); + + PopulateEntryArrayL(iEntryArray); + } + +/** +It deletes all the enumerated SIM messages stored in re-assembly store. +This function will be called if user choses to cancel the enumeration. + +@internalComponent +*/ +void CReassemblyStore::DeleteEnumeratedSIMEntries() + { + const TInt count = iEntryArray.Count(); + + LOGSMSPROT2("CReassemblyStore::DeleteEnumeratedSIMEntries(): %d messages in RAS", count); + + TInt index; + + for (index = count-1; index >= 0; --index) + { + TReassemblyEntry entry = iEntryArray[index]; + + if (entry.Storage()==CSmsMessage::ESmsSIMStorage) + { + TRAP_IGNORE(BeginTransactionLC(); + DeleteEntryL(entry); + CommitTransactionL(); + iEntryArray.Delete(index)); + } + } + } + +/** +It returns the number of complete messages in reassembly store. + +@internalComponent +*/ +TInt CReassemblyStore::NumberOfCompleteMessages() + { + LOGSMSPROT2("CReassemblyStore::NumberOfCompleteMessages(): iEntryArray.Count()=%d", + iEntryArray.Count()); + + //local variable for complete entries + TInt count( 0 ); + // checks all entrys in the reassembly store + for ( TInt i = iEntryArray.Count()-1; i >= 0; i-- ) + { + // checks if entry is completed + if ( iEntryArray[i].IsComplete() ) + { + ++count; + } + } + return count; + } + +/** +It adds the message segment to the reassembly store. There are 5 possiblities: + +1) This is the single segment message. +We therefore have all the segments. +2) This is a duplicate message segment. +We will ignore it. +3) This is the last segment in the message required to complete it. +The other segments are already stored. +4) This is another PDU to an existing message in the store, but it is +not yet complete. +5) This is the first PDU in the message, and therefore the message is +not yet complete and no segments are stored. + +@note Only SUBMIT or DELIVER PDUs can be added to the reassembly store. + +@param aSmsMessage a reference to the SMS message. + It acts both as input & output. If the message is complete, it contains the decoded message. + Otherwise it contains the received message with few properties set (LogServerId, Time). + +@param aGsmSms a reference to GsmSms object which contain actual PDU. + It acts as input. + +@param aIsComplete Boolean value indicating whether the message is complete or not. + It acts both as input & output. + +@param aIsEnumeration Boolean value indicating whether the function is called at the time of enumeration. + It acts as only input. + +@param aCount value indicating the number of current PDUs in the re-assembly store for the given SMS message. + It acts as only output. + +@param aTotal value indicating the total number of PDUs in the re-assembly store for the given SMS message. + It acts as only output. + +@internalComponent +*/ +void CReassemblyStore::AddSegmentToReassemblyStoreL(CSmsMessage& aSmsMessage,const TGsmSms& aGsmSms, TInt& aIndex, TBool& aIsComplete, TBool aIsEnumeration, TInt& aCount, TInt& aTotal) + { + LOGSMSPROT2("CReassemblyStore::AddSegmentToReassemblyStoreL(): isComplete Message=%d", + aSmsMessage.IsComplete()); + + /* + (1) If it is a single segment message create a new message + (2) If it is part of concatenated message find whether this is the first PDU + or it is the part of existing message. + If it is a new message then create new one. + If it is part of existing message, then check duplication (CheckDuplication()). + If it is a duplicate, return. Otherwise update the reassembly store. + */ + + if (aIsComplete || aSmsMessage.Type() == CSmsPDU::ESmsStatusReport) + { + // + // 1) This is the complete message (e.g. a single-segment message). + // We therefore have all the segments. + // + // Create the new message in the reassembly store. This is incase the + // power fails before the client gets it (note that it will be ack'd + // before passed to the client) so keeping it in memory is not + // acceptable... + // + NewMessagePDUL(aIndex, aSmsMessage, aGsmSms); + } + else + { + // + // If not yet complete, then we must be part of a multiple PDU message. + // Search the reassembly store for existing parts of the message. + // + TInt segStoreIndex(KErrNotFound); + + MatchPDUToExistingMessage(aSmsMessage, segStoreIndex); + LOGSMSPROT2("CSmsReassemblyStore::AddSegmentToReassemblyStoreL(): " + "segStoreIndex=%d", segStoreIndex); + + // + // If not yet complete, then we must be part of a multiple PDU message. + // Search the reassembly store for existing parts of the message. This + // may set iIsComplete to true if all segments are then found. + // + if (segStoreIndex != KErrNotFound) + { + TBool isDuplicateSlot(EFalse); + TBool isDuplicateMsgRef(EFalse); + // + // So we found a related part of the message, add this message to the + // store... + // + aIndex = segStoreIndex; + UpdateExistingMessageL(aSmsMessage, aGsmSms, aIndex, + aIsComplete, isDuplicateMsgRef, + isDuplicateSlot); + LOGSMSPROT5("CSmsReassemblyStore::AddSegmentToReassemblyStoreL(): " + "aIndex=%d, isComplete=%d, isDuplicateMsgRef=%d, isDuplicateSlot=%d", + aIndex, aIsComplete, isDuplicateMsgRef, isDuplicateSlot); + + if (isDuplicateMsgRef) + { + // + // In most cases discard it, unless we are doing an enumeration??? + // + if (aIsEnumeration) + { + NewMessagePDUL(aIndex, aSmsMessage, aGsmSms); + } + } + else if (aIsComplete) + { + // + // 3) This is the last segment in the message required to complete it. + // The other segments are already stored. + // + // Load the complete message into memory for futher processing. + // + GetMessageL(aIndex, aSmsMessage); + } + else + { + // + // 4) This is another PDU to an existing message in the store, but it is + // not yet complete. + // + // Update the this segment with the timestamp of the original message. + // + CSmsBuffer* buffer = CSmsBuffer::NewL(); + CSmsMessage* firstMessagePdu = CSmsMessage::NewL(iFs, + CSmsPDU::ESmsDeliver, buffer); + CleanupStack::PushL(firstMessagePdu); + GetMessageL(aIndex, *firstMessagePdu); + aSmsMessage.SetUTCOffset(firstMessagePdu->UTCOffset()); + CleanupStack::PopAndDestroy(firstMessagePdu); + } + } + else + { + // + // 5) This is the first PDU in the message, and therefore the message is + // not yet complete and no segments are stored. + // + // The entry needs to be added to the reassembly store as a new entry. + // + NewMessagePDUL(aIndex, aSmsMessage, aGsmSms); + } + } + + const TReassemblyEntry& entry = iEntryArray[aIndex]; + aCount = entry.Count(); + aTotal = entry.Total(); + } + +/** +It deletes the given SMS message from re-assembly store. + +@param aSmsMessage Message to delete. +@param aPassed Determines if we are searching for a message already + passed to the client. + +@internalComponent +*/ +void CReassemblyStore::DeleteMessageL(const CSmsMessage& aSmsMessage, TBool aPassed) + { + LOGSMSPROT1("CReassemblyStore::DeleteMessageL()"); + TInt index(0); + BeginTransactionLC(); + if (FindMessageL(aSmsMessage, aPassed, index)) + { + const TReassemblyEntry& entry = iEntryArray[index]; + DeleteEntryL(entry); + iEntryArray.Delete(index); + } + CommitTransactionL(); + } + +/** +It updates log server id of the passed message in re-assembly store. + +@param aSmsMessage a reference to a message. +@param aIndex index number of sms message to be updated. + +@internalComponent +*/ +void CReassemblyStore::UpdateLogServerIdOfMessageL(const CSmsMessage& aSmsMessage, TInt aIndex) + { + LOGSMSPROT1("CReassemblyStore::UpdateLogServerIdOfMessageL()"); + TInt foundIndex(KErrNotFound); + TBool found(EFalse); + + BeginTransactionLC(); + + found = FindMessageL(aSmsMessage , EFalse, foundIndex); + if (found && (aIndex == foundIndex)) + { + const TReassemblyEntry& entry = iEntryArray[foundIndex]; + UpdateLogServerIdL(entry, aSmsMessage.LogServerId()); + iEntryArray[foundIndex].SetLogServerId(aSmsMessage.LogServerId()); + } + CommitTransactionL(); + } + +/** +It updates that the given SMS message in re-assembly store is passed to client. + +@param aSmsMessage Message which is passed to client. + +@internalComponent +*/ +void CReassemblyStore::SetMessagePassedToClientL(const CSmsMessage& aSmsMessage, TBool aPassed) + { + LOGSMSPROT1("CReassemblyStore::SetMessagePassedToClientL()"); + TInt index(0); + + BeginTransactionLC(); + + if (FindMessageL(aSmsMessage , !aPassed, index)) + { + const TReassemblyEntry& entry = iEntryArray[index]; + SetPassedToClientL(entry, aPassed); + iEntryArray[index].SetPassedToClient(aPassed); + } + CommitTransactionL(); + } + +/** +It adds a new message segment to the reassembly store and it returns an index to the message. + +@param aIndex value indicating the index of the message added to re-assembly store. + It acts as output. + +@param aSmsMessage a reference to the SMS message. + It acts as input. + +@param aGsmSms a reference to GsmSms object which contain actual PDU. + It acts as input. + +@internalComponent +*/ +void CReassemblyStore::NewMessagePDUL(TInt& aIndex,CSmsMessage& aSmsMessage,const TGsmSms& aGsmSms) + { + LOGSMSPROT1("CReassemblyStore::NewMessagePDUL"); + + if (aSmsMessage.Time() >= iLastRealTime) + { + iLastRealTime=aSmsMessage.Time(); + if(iLastReceivedTime >= aSmsMessage.Time()) + { + aSmsMessage.SetTime(iLastReceivedTime+(TTimeIntervalMicroSeconds32)1); + } + iLastReceivedTime=aSmsMessage.Time(); //provide uniqueness of time + } + else // clock turned back + { + iLastReceivedTime=aSmsMessage.Time(); + } + + TReassemblyEntry entry; + CReassemblyStoreUtility::PopulateEntry(entry,aSmsMessage,1); + BeginTransactionLC(); + AddNewMessageL(aSmsMessage, aGsmSms); + CommitTransactionL(); + //Successfully added so add the entry in entry array. + aIndex = iEntryArray.Count(); + iEntryArray.AppendL(entry); + } + +/** +It adds a new message segment to the existing message in reassembly store & returns +whether this segment makes this message complete or not. It also returns whether this +segment is the duplicate one or not. + +@param aSmsMessage a reference to the SMS message. + It acts as input. + +@param aGsmSms a reference to GsmSms object which contain actual PDU. + It acts as input. + +@param aIndex value indicating the index of the message added to re-assembly store. + It acts as output. + +@param aIsComplete Boolean value indicating whether the message is complete or not. + It acts as output. + +@param aDuplicateMsgRef Boolean value indicating whether the added segment is a duplicate one or not. + It acts as output. + +@param aDuplicateSlot Boolean value indicating whether the added segment is from duplicate slot or not. + It acts as output. + +@internalComponent +*/ +void CReassemblyStore::UpdateExistingMessageL(CSmsMessage& aSmsMessage,const TGsmSms& aGsmSms, + TInt aIndex, TBool& aIsComplete, + TBool& aDuplicateMsgRef, TBool& aDuplicateSlot) + { + LOGSMSPROT1("CReassemblyStore::UpdateExistingMessageL"); + aIsComplete = EFalse; + BeginTransactionLC(); + UpdateExistingMessageL(aSmsMessage, aGsmSms, aDuplicateMsgRef, aDuplicateSlot); + CommitTransactionL(); + if ((aDuplicateMsgRef == EFalse) && (aDuplicateSlot==EFalse)) + { + iEntryArray[aIndex].SetCount(iEntryArray[aIndex].Count() + 1); + if (iEntryArray[aIndex].IsComplete()) + { + aIsComplete = ETrue; + } + } + } + +/** +It matches the passed message in re-assembly store & returns the index. + +@param aSmsMessage a reference to the SMS message. + It acts as input. + +@param aIndex index number of message in re-assembly store. + It acts as input. + +@internalComponent +*/ +void CReassemblyStore::MatchPDUToExistingMessage(const CSmsMessage& aSmsMessage, + TInt& aIndex) + { + LOGSMSPROT1("CReassemblyStore::MatchPDUToExistingMessage()"); + + aIndex = KErrNotFound; + + TGsmSmsTelNumber parsedAddress; + aSmsMessage.ParsedToFromAddress(parsedAddress); + + // + // Search the reassembly store for a matching entry (start from the + // end as the most recent PDUs appear at the end)... + // + TInt reassemblyCount = iEntryArray.Count(); + + for (TInt index = 0; index < reassemblyCount; index++) + { + TReassemblyEntry& entry = iEntryArray[index]; + // Always check the fields in order of the quickest to check... + if (entry.IsComplete() == EFalse && + entry.PduType() == aSmsMessage.Type() && + entry.Storage() == aSmsMessage.Storage()) + { + TInt telLen = Min(entry.Description2().Length(), + parsedAddress.iTelNumber.Length()); + + if (entry.Description2().Right(telLen) == parsedAddress.iTelNumber.Right(telLen) && + entry.Total() == aSmsMessage.SmsPDU().NumConcatenatedMessagePDUs() && + entry.Reference() == aSmsMessage.SmsPDU().ConcatenatedMessageReference()) + { + // + // Found it! + // + aIndex = index; + break; + } + } + } + + LOGSMSPROT3("CReassemblyStore::MatchPDUToExistingMessage(): reassemblyCount=%d, aIndex=%d", reassemblyCount, aIndex); + } // CReassemblyStore::MatchPDUToExistingMessage + +/** +It retrieves the message from re-assembly store. + +@param aIndex index number of message in re-assembly store. + It acts as input. + +@param aSmsMessage a reference to the SMS message. + It acts as output. + +@internalComponent +*/ +void CReassemblyStore::GetMessageL(TInt aIndex, CSmsMessage& aSmsMessage) + { + LOGSMSPROT1("CReassemblyStore::GetMessageL()"); + const TReassemblyEntry& entry = iEntryArray[aIndex]; + RetrieveMessageL(entry, aSmsMessage); + } + +/** + * Searches the reassembly store for a CSmsMessage with aPassed value (indicates if we + * are searching for a message already passed to client) and returns its index. + * + * @param aSmsMessage Message to search for. + * @param aPassed Determines if we are searching for a message already + * passed to the client. + * @param aIndex Return index value. + * + * @return True and an index if aSmsMessage is found in this reassembly store + */ +TBool CReassemblyStore::FindMessageL(const CSmsMessage& aSmsMessage, + TBool aPassed, + TInt& aIndex) + { + LOGSMSPROT1("CReassemblyStore::FindMessageL()"); + + // + // Parse the GSM data from the SMS message... + // + TGsmSmsTelNumber parsedAddress; + + aSmsMessage.ParsedToFromAddress(parsedAddress); + + // + // Search the store for a matching message... + // + for (TInt index = iEntryArray.Count() - 1; index >= 0; index--) + { + const TReassemblyEntry& entry = iEntryArray[index]; + + // Always search the basic types first and strings last! + if (entry.PduType() == aSmsMessage.Type() && + entry.PassedToClient() == aPassed && + entry.Storage() == aSmsMessage.Storage() && + entry.Time() == aSmsMessage.Time() && + entry.Description2().Right(8) == parsedAddress.iTelNumber.Right(8)) + { + // + // Found! + // + LOGSMSPROT2("CReassemblyStore::FindMessage(): Found! index=%d", index); + + aIndex = index; + + return ETrue; + } + } + + // + // Not found... + // + LOGSMSPROT1("CReassemblyStore::FindMessage(): Not found!"); + + return EFalse; + } // CReassemblyStore::FindMessageL