diff -r 000000000000 -r 3553901f7fa8 smsprotocols/smsstack/smsprot/Src/smspclass0stor.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/smsprotocols/smsstack/smsprot/Src/smspclass0stor.cpp Tue Feb 02 01:41:59 2010 +0200 @@ -0,0 +1,3561 @@ +// 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: +// Implements CPreallocatedFile. +// +// + +/** + @file +*/ +#include "smspclass0stor.h" +#include "gsmubuf.h" +#include "gsmunonieoperations.h" + +const TInt KSizeOfPreallocatedFileVersion = sizeof(CPreallocatedFile::TPreAllocatedFileVersion); +const TInt KSizeOfNumberOfEntrySection = sizeof(TInt); +//size of 32-bit checksum +const TInt KSizeOfChecksum = sizeof(TUint32); +const TInt KSizeOfPreAllocatedStoreEntry = sizeof(TSmsPreAllocatedFileStoreReassemblyEntry); +const TInt KSizeOfGsmSmsSlotEntry = sizeof(TGsmSmsSlotEntry); +const TInt KSizeOfIndexEntry = sizeof(TInt); +const TInt KSizeOfSmsGsmPDU = sizeof(RMobileSmsMessaging::TMobileSmsGsmTpdu); +const TInt KSizeOfAContainer = KSizeOfGsmSmsSlotEntry + KSizeOfIndexEntry + KSizeOfSmsGsmPDU; +const TInt KBeginOfMasterHeaderSection = KSizeOfPreallocatedFileVersion; + +/** +Static factory constructor. Uses two phase +construction and leaves nothing on the CleanupStack. + +@param aFs File Server handle. +@param aFileName Permanent file store name. +@param aThirdUid Uid of file. +@leave KErrNoMemory + +@return A pointer to the newly created CSmsPermanentFileStore object. + +@pre A connected file server session must be passed as parameter. +@post CSmsPermanentFileStore object is now fully initialised + +@internalComponent +*/ +CSmsPermanentFileStore* CSmsPermanentFileStore::NewL(RFs& aFs, const TDesC& aFileName, const TUid& aThirdUid) + { + LOGSMSPROT1("CSmsPermanentFileStore::NewL()"); + CSmsPermanentFileStore* self = new (ELeave) CSmsPermanentFileStore(aFs, aThirdUid); + CleanupStack::PushL(self); + self->ConstructL(aFileName); + CleanupStack::Pop(self); + + return self; + } + +void CSmsPermanentFileStore::ConstructL(const TDesC& aFileName) + { + LOGSMSPROT1("CSmsPermanentFileStore::ConstructL()"); + iFileName = aFileName.AllocL(); + } + +/** + * Constructor. +*/ +CSmsPermanentFileStore::CSmsPermanentFileStore(RFs& aFs, const TUid& aThirdUid) + : iFs(aFs), iFileName(NULL), iThirdUid(aThirdUid), iFileStore(NULL), iEntryArray(KFlatArrayGranularity) + { + } + +/** + * Destructor. It destroys all the member variables. +*/ +CSmsPermanentFileStore::~CSmsPermanentFileStore() + { + delete iFileName; + } + +/** +It checks & returns whether the file exist or not. If file is there whether +it is corrupted or not. + +@internalComponent +*/ +TBool CSmsPermanentFileStore::IsFileOK() + { + TBool retBool(EFalse); + TUidType uidtype(KPermanentFileStoreLayoutUid,KSARStoreUid,iThirdUid); + TEntry entry; + TInt ret=iFs.Entry(iFileName->Des(), entry); + // Check file existence & corruption + if ((ret == KErrNone) && (entry.iType == uidtype)) + { + retBool = ETrue; + } + return retBool; + } + +/** +It creates a permanent store file. + +@internalComponent +*/ +void CSmsPermanentFileStore::CreateL() + { + LOGSMSPROT1("CSmsPermanentFileStore::CreateL()"); + TUidType uidtype(KPermanentFileStoreLayoutUid,KSARStoreUid,iThirdUid); + iFileStore=CPermanentFileStore::ReplaceL(iFs, iFileName->Des(), EFileShareExclusive|EFileStream|EFileRead|EFileWrite); + iFileStore->SetTypeL(uidtype); + iEntryArray.Reset(); + ExternalizeEntryArrayL(); + iFileStore->CommitL(); + // Close it to make sure that file is created correctly (defensive approach). + Close(); + //Again Open the file + OpenL(); + } + +/** +It opens the permanent store file and internalizes the entries. + +@internalComponent +*/ +void CSmsPermanentFileStore::OpenL() + { + LOGSMSPROT1("CSmsPermanentFileStore::OpenL()"); + iFileStore=CPermanentFileStore::OpenL(iFs,iFileName->Des(),EFileShareExclusive|EFileStream|EFileRead|EFileWrite); + InternalizeEntryArrayL(); + } + +/** +It closes the permanent store file. + +@internalComponent +*/ +void CSmsPermanentFileStore::Close() + { + LOGSMSPROT1("CSmsPermanentFileStore::Close()"); + delete iFileStore; + iFileStore = NULL; + iEntryArray.Reset(); + } + +/* +This function cleans its entries against the passed entries. +This is needed because there might be a scenario where user has deleted a message +but the corresponding message is not deleted from permanent store file +due to out-of-disk condition. But the entry is invalid because +it is no more in the pre-allocated file which contains master header info. +And also at the time of forwarding an incomplete message a forwarded +message has not been deleted due to above reason. +This function also compacts the store after deletion. + +@param aEntryArray entray array against whose clean up is carried out. + +@internalComponent +*/ +void CSmsPermanentFileStore::CleanupEntriesWithCompactL(const CArrayFix& aEntryArray) + { + // Ignore in code coverage - a previous CleanupEntries would need to have failed with KErrDiskFull + BULLSEYE_OFF + LOGSMSPROT1("CSmsPermanentFileStore::CleanupEntriesWithCompactL()"); + + iCompact = ETrue; + CleanupEntriesL(aEntryArray); + BULLSEYE_RESTORE + } + +/* +This function cleans its entries against the passed entries. +This is needed because there migth be a scenario where user has deleted a message +but the corresponding message is not deleted from permanent store file +due to out-of-disk condition. But the entry is invalid because +it is no more in the pre-allocated file which contains master header info. +And also at the time of forwarding an incomplete message a forwarded +message has not been deleted due to above reason. + +@param aEntryArray entray array against whose clean up is carried out. + +@internalComponent +*/ +void CSmsPermanentFileStore::CleanupEntriesL(const CArrayFix& aEntryArray) + { + LOGSMSPROT1("CSmsPermanentFileStore::CleanupEntriesL()"); + + TInt reassemblyCount = iEntryArray.Count(); + TInt index, index2; + + for (index = 0; index < reassemblyCount; index++) + { + for (index2 = 0; index2 < aEntryArray.Count(); index2++) + { + if (iEntryArray[index].Reference() == aEntryArray[index2].Reference() && + iEntryArray[index].Total() == aEntryArray[index2].Total() && + iEntryArray[index].PduType() == aEntryArray[index2].PduType() && + iEntryArray[index].Storage() == aEntryArray[index2].Storage() && + iEntryArray[index].Description2() == aEntryArray[index2].Description2()) + { + if (aEntryArray[index2].NumberOfPDUsForwardToClient() > 0) + { + //Internalize the entries. + CSmsBuffer* buffer = CSmsBuffer::NewL(); + CSmsMessage* smsMessage = CSmsMessage::NewL(iFs, CSmsPDU::ESmsDeliver, buffer); + CleanupStack::PushL(smsMessage); + + CArrayFix* indexArray = new(ELeave) CArrayFixFlat(KFlatArrayGranularity); + CleanupStack::PushL(indexArray); + CArrayFixFlat* smsArray = new(ELeave) CArrayFixFlat(KFlatArrayGranularity); + CleanupStack::PushL(smsArray); + + InternalizeEntryL(index, *smsMessage, *indexArray, *smsArray); + TInt noOfForwardedEntries = 0; + for (TInt j=indexArray->Count(); j>0 ;j--) + { + TUint8 bitMapIndex = (indexArray->At(j-1)-1)/8; + TUint8 bitPos = (indexArray->At(j-1)-1)%8; + TUint8 bitMap; + TSmsPreAllocatedFileStoreReassemblyEntry entry; + entry = aEntryArray[index2]; + entry.GetBitMap(bitMapIndex, bitMap); + TUint8 tmpBitMap = 1; + tmpBitMap <<= bitPos; + if (tmpBitMap == (bitMap & tmpBitMap)) + { + noOfForwardedEntries++; + indexArray->Delete(j-1); + smsArray->Delete(j-1); + } + } + if (noOfForwardedEntries > 0) + { + TStreamId streamId = iEntryArray[index].DataStreamId(); + ExternalizeEntryL(streamId, *smsMessage, *indexArray, *smsArray); + } + CleanupStack::PopAndDestroy(3, smsMessage); // smsMessage, indexArray, smsArray + } + break; + } + } + if (index2 == aEntryArray.Count()) + { + DeleteEntryL(index); + } + } + } + +/** +It internalizes its entry array. + +@internalComponent +*/ +void CSmsPermanentFileStore::InternalizeEntryArrayL() + { + LOGSMSPROT1("CSmsPermanentFileStore::InternalizeEntryArrayL()"); + + iEntryArray.Reset(); + TStreamId headerid=iFileStore->Root(); + RStoreReadStream stream; + stream.OpenLC(*iFileStore,headerid); + TInt count=stream.ReadInt32L(); + for (TInt i=0; i> sarentry; + iEntryArray.AppendL(sarentry); + } + CleanupStack::PopAndDestroy(); // stream + } + +/** +It externalizes its entry array. + +@internalComponent +*/ +void CSmsPermanentFileStore::ExternalizeEntryArrayL() + { + LOGSMSPROT4("CSmsPermanentFileStore::ExternalizeEntryArrayL(): this=0x%08X count=%d headerid=%d]", + this, iEntryArray.Count(), iFileStore->Root().Value()); + + TStreamId headerid=iFileStore->Root(); + RStoreWriteStream stream; + if (headerid==KNullStreamId) + { + headerid=stream.CreateLC(*iFileStore); + iFileStore->SetRootL(headerid); + } + else + { + stream.ReplaceLC(*iFileStore,headerid); + } + + TInt count1=iEntryArray.Count(); + TInt count2=0; + TInt i=0; + + for (; i* indexArray=new(ELeave) CArrayFixFlat(KFlatArrayGranularity); + CleanupStack::PushL(indexArray); + CArrayFixFlat* smsArray=new(ELeave) CArrayFixFlat(KFlatArrayGranularity); + CleanupStack::PushL(smsArray); + //Index must initialize to 0. + TInt index = 0; + if (!aSmsMessage.IsDecoded()) + { + index = aSmsMessage.SmsPDU().ConcatenatedMessagePDUIndex(); + } + indexArray->AppendL(index); + smsArray->AppendL(aGsmSms); + + TStreamId streamid = KNullStreamId; + ExternalizeEntryL(streamid, aSmsMessage, *indexArray, *smsArray); + + TSmsReassemblyEntry tmpEntry; + //Fill-up entry information... + CReassemblyStoreUtility::PopulateEntry(tmpEntry, aSmsMessage, 1); + // Update Data stream id. + tmpEntry.SetDataStreamId(streamid); + + aIndex = iEntryArray.Count(); + AddEntryL(tmpEntry); + CleanupStack::PopAndDestroy(2); //indexArray, smsArray + } + +/* +It updates the existing message in permanent store file. + +@param aIndex index number on which message is to be updated. +@param aSmsMessage reference to sms message to be updated. +@param aIndexArray array of index of all the PDUs. +@param aSmsArray array of sms of all the PDUs. + +@internalComponent +*/ +void CSmsPermanentFileStore::UpdateExistingMessageL(TInt aIndex, const CSmsMessage& aSmsMessage,const CArrayFix& aIndexArray,const CArrayFix& aSmsArray) + { + LOGSMSPROT1("CSmsPermanentFileStore::UpdateExistingMessageL()"); + TStreamId streamid = iEntryArray[aIndex].DataStreamId(); + ExternalizeEntryL(streamid, aSmsMessage, aIndexArray, aSmsArray); + TSmsReassemblyEntry entry; + CReassemblyStoreUtility::PopulateEntry(entry, aSmsMessage, aSmsArray.Count()); + entry.SetDataStreamId(streamid); + ChangeEntryL(aIndex, entry); + } + +/* +It matches the entry with entry in permanent store file & returns +the index. If not found it returns KErrNotFound. + +@param aEntry reference to entry information. +@param aIndex (output) returns the index number. + +@internalComponent +*/ +void CSmsPermanentFileStore::MatchEntryToExistingMessage(const TReassemblyEntry& aEntry, + TInt& aIndex) + { + LOGSMSPROT1("CSmsPermanentFileStore::MatchEntryToExistingMessage()"); + + aIndex = KErrNotFound; + + // + // Search the reassembly store for a matching entry... + // + TInt reassemblyCount = iEntryArray.Count(); + for (TInt index = 0; index < reassemblyCount; index++) + { + TSmsReassemblyEntry& entry = iEntryArray[index]; + + if (entry.Reference() == aEntry.Reference() && + entry.Total() == aEntry.Total() && + entry.PduType() == aEntry.PduType() && + entry.Storage() == aEntry.Storage() && + entry.Description2() == aEntry.Description2()) + { + // + // Found it! + // + aIndex = index; + break; + } + } + + LOGSMSPROT2("CSmsPermanentFileStore::MatchEntryToExistingMessage(): aIndex=%d", aIndex); + } + +/* +It updates the log server id of the message. + +@param aIndex index number of the message to be updated. +@param aLogServerId log server id. + +@internalComponent +*/ +void CSmsPermanentFileStore::UpdateLogServerIdL(TInt& aIndex, TLogId aLogServerId) + { + LOGSMSPROT1("CSmsPermanentFileStore::UpdateLogServerIdL"); + + TSmsReassemblyEntry entry; + entry = iEntryArray[aIndex]; + + if (entry.LogServerId() != aLogServerId) + { + entry.SetLogServerId(aLogServerId); + ChangeEntryL(aIndex, entry); + } + } + +/* +It sets the message passed to client or not. + +@param aIndex index number of the message to be updated. +@param aBool boolean value indicating whether message is passes or not. + +@internalComponent +*/ +void CSmsPermanentFileStore::SetPassedToClientL(TInt aIndex, TBool aBool) + { + LOGSMSPROT2("CSmsPermanentFileStore::SetPassedToClientL(): aIndex=%d", aIndex); + + TSmsReassemblyEntry entry; + entry = iEntryArray[aIndex]; + + if (entry.PassedToClient() != aBool) + { + entry.SetPassedToClient(aBool); + ChangeEntryL(aIndex, entry); + } + } + +/* +It adds the new entry in the existing entry array. + +@param aEntry entry to be added. + +@internalComponent +*/ +void CSmsPermanentFileStore::AddEntryL(TSmsReassemblyEntry& aEntry) + { + iEntryArray.AppendL(aEntry); + iEntryArray[iEntryArray.Count()-1].SetIsAdded(ETrue); + } + +/* +It changes the existing entry with new entry. + +@param aIndex index number of the entry which will be changed. +@param aNewEntry entry to be updated. + +@internalComponent +*/ +void CSmsPermanentFileStore::ChangeEntryL(TInt aIndex,const TSmsReassemblyEntry& aNewEntry) + { + LOGSMSPROT2("CSmsPermanentFileStore::ChangeEntryL(): aIndex=%d", aIndex); + + iEntryArray[aIndex].SetIsDeleted(ETrue); + iEntryArray.InsertL(aIndex,aNewEntry); + iEntryArray[aIndex].SetIsAdded(ETrue); + } + +/* +It deletes the entry. + +@param aIndex index number of the entry which will be deleted. + +@internalComponent +*/ +void CSmsPermanentFileStore::DeleteEntryL(TInt aIndex) + { + iFileStore->DeleteL(iEntryArray[aIndex].DataStreamId()); + iEntryArray[aIndex].SetIsDeleted(ETrue); + } + +/* +It externalizes(writes) the entry in permanent store file. + +@param aStreamId stream id which needs to externalized. +@param aSmsMessage reference to sms message which needs to be externalized. +@param aIndexArray refence to array of index which needs to be externalized. +@param aSmsArray refence to array of sms which needs to be externalized. + +@internalComponent +*/ +void CSmsPermanentFileStore::ExternalizeEntryL(TStreamId& aStreamId,const CSmsMessage& aSmsMessage,const CArrayFix& aIndexArray,const CArrayFix& aSmsArray) + { + LOGSMSPROT2("CSmsPermanentFileStore::ExternalizeEntryL Start [sid=%d]", aStreamId.Value()); + + RStoreWriteStream writestream; + if (aStreamId==KNullStreamId) + aStreamId=writestream.CreateLC(*iFileStore); + else + writestream.ReplaceLC(*iFileStore,aStreamId); + writestream << aSmsMessage; + TInt count=aIndexArray.Count(); + writestream.WriteInt32L(count); + TInt i=0; + for (; i& aIndexArray, CArrayFix& aSmsArray) + { + TSmsReassemblyEntry& entry = iEntryArray[aIndex]; + LOGSMSPROT2("CSmsPermanentFileStore::InternalizeEntryL Start [sid=%d]", entry.DataStreamId().Value()); + RStoreReadStream readstream; + readstream.OpenLC(*iFileStore, entry.DataStreamId()); + readstream >> aSmsMessage; + TInt count=readstream.ReadInt32L(); + TInt i; + for (i=0; i> pdu; + TGsmSms sms; + sms.SetPdu(pdu); + aSmsArray.AppendL(sms); + } + CleanupStack::PopAndDestroy(); + //Set other properties of CSmsMessage + aSmsMessage.SetStorage(entry.Storage()); + aSmsMessage.SetLogServerId(entry.LogServerId()); + aSmsMessage.SetTime(entry.Time()); + LOGSMSPROT2("CSmsPermanentFileStore::InternalizeEntryL End [count=%d]", count); + } + +/* +It removes the PDUs from permanent store file. +This function is needed because after forwarding the incomplete message +to client, the corresponding PDUs needs to be be removed from permanent +store file. +This functionality is specific to class 0 re-assembly store. + +@param aIndex index number of the message to be removed. +@param aStartPos starting pos of pdu to be removed. +@param aEndPos end pos of pdu to be removed. + +@internalComponent +*/ +void CSmsPermanentFileStore::RemovePDUsL(TInt aIndex, TInt aStartPos, TInt aEndPos) + { + LOGSMSPROT1("CSmsPermanentFileStore::RemovePDUsL"); + + CSmsBuffer* buffer = CSmsBuffer::NewL(); + CSmsMessage* smsMessage = CSmsMessage::NewL(iFs, CSmsPDU::ESmsDeliver, buffer); + CleanupStack::PushL(smsMessage); + + CArrayFix* indexArray = new(ELeave) CArrayFixFlat(KFlatArrayGranularity); + CleanupStack::PushL(indexArray); + CArrayFixFlat* smsArray = new(ELeave) CArrayFixFlat(KFlatArrayGranularity); + CleanupStack::PushL(smsArray); + + InternalizeEntryL(aIndex, *smsMessage, *indexArray, *smsArray); + + TInt count = indexArray->Count(); + for (TInt i=count; i>0; i--) + { + if ((indexArray->At(i-1) >= aStartPos) && (indexArray->At(i-1) <= aEndPos)) + { + indexArray->Delete(i-1); + smsArray->Delete(i-1); + } + } + + /* + There are 3 scenarios in this case: + 1. If all the entries are removed, + then there is no need to store the entry in this permanent file. + 2. If few entries are removed, + then externalize the remaining entries. Update count field also. + 3. If no entries are removed, then do nothing. + */ + if (indexArray->Count()==0) + { + DeleteEntryL(aIndex); + } + else if (count!=indexArray->Count()) + { + TStreamId streamid = iEntryArray[aIndex].DataStreamId(); + ExternalizeEntryL(streamid, *smsMessage, *indexArray, *smsArray); + + TSmsReassemblyEntry entry; + entry = iEntryArray[aIndex]; + entry.SetCount(indexArray->Count()); + ChangeEntryL(aIndex, entry); + } + CleanupStack::PopAndDestroy(3, smsMessage); // smsMessage, indexArray, smsArray + } + +/** + * Sets the permanent store as in-transaction. + * + * The function checks the validity of the call and leaves KErrAccessDenied if + * invalid. + * @capability None + */ +void CSmsPermanentFileStore::BeginTransactionL() + { + LOGSMSPROT4("CSmsPermanentFileStore::BeginTransactionL [this=0x%08X iInTransaction=%d iFileStore=0x%08X]", this, iInTransaction, iFileStore); + + if (iFileStore == NULL || iInTransaction) + { + LOGSMSPROT1("WARNING CSmsPermanentFileStore::BeginTransactionL leaving with KErrAccessDenied"); + User::Leave(KErrAccessDenied); + } + + iInTransaction = ETrue; + } // CSmsPermanentFileStore::BeginTransactionL + +/** + * It reverts the transaction. + */ +void CSmsPermanentFileStore::Revert() + { + LOGSMSPROT3("CSmsPermanentFileStore::Revert(): this=0x%08X, iInTransaction=%d", + this, iInTransaction); + + iFileStore->Revert(); + iInTransaction = EFalse; + ReinstateDeletedEntries(); + } // CSmsPermanentFileStore::Revert + +/** + * It commits the transaction. Then it compact the permanent store file. + */ +void CSmsPermanentFileStore::DoCommitAndCompactL() + { + LOGSMSPROT1("CSmsPermanentFileStore::DoCommitAndCompactL()"); + + LOGSMSPROTTIMESTAMP(); + iFileStore->CommitL(); + LOGSMSPROTTIMESTAMP(); + + iCommitCount--; + if ((iCommitCount < 0) || (iCompact)) + { + iCommitCount = KNumStoreCommitsBeforeCompaction; + iFileStore->CompactL(); + iFileStore->CommitL(); + iCompact = EFalse; + } + } // CSmsPermanentFileStore::DoCommitAndCompactL + +/** + * It commits the transaction. + */ +void CSmsPermanentFileStore::CommitTransactionL() + { + LOGSMSPROT4("CSmsPermanentFileStore::CommitTransactionL(): this=0x%08X iInTransaction=%d iFileStore=0x%08X", + this, iInTransaction, iFileStore); + + ExternalizeEntryArrayL(); + +#ifdef _SMS_LOGGING_ENABLED + TRAPD(err, DoCommitAndCompactL()); + if (err != KErrNone) + { + LOGGSMU2("WARNING! could not CommitL/CompactL due to %d", err); + User::Leave(err); + } +#else + DoCommitAndCompactL(); +#endif + + iInTransaction = EFalse; + RemoveDeletedEntries(); + } + +/** + * It removes the deleted entries from entry arry. + * This function is called after commit. + */ +void CSmsPermanentFileStore::RemoveDeletedEntries() + { + LOGSMSPROT1("CSmsPermanentFileStore::RemoveDeletedEntries()"); + + TInt count=iEntryArray.Count(); + while (count--) + { + TSmsReassemblyEntry& entry = iEntryArray[count]; + + if (entry.IsDeleted()) + { + iEntryArray.Delete(count); + } + else + { + entry.SetIsAdded(EFalse); + } + } + } // CSmsPermanentFileStore::RemoveDeletedEntries + +/** + * It reinstate the deleted/added entries from entry arry. + * This function is called after revert operation. + */ +void CSmsPermanentFileStore::ReinstateDeletedEntries() + { + LOGSMSPROT1("CSmsPermanentFileStore::ReinstateDeletedEntries()"); + + TInt count=iEntryArray.Count(); + while (count--) + { + TSmsReassemblyEntry& entry = iEntryArray[count]; + + if (entry.IsAdded()) + { + iEntryArray.Delete(count); + } + else + { + entry.SetIsDeleted(EFalse); + } + } + } // CSmsPermanentFileStore::ReinstateDeletedEntries + +/** + * Constructor + * + * @capability None + */ +TSmsPreAllocatedFileStoreReassemblyEntry::TSmsPreAllocatedFileStoreReassemblyEntry(): + TSmsReassemblyEntry(), + iPreAllocatedStorageId(0), + iStatus(RMobileSmsStore::EStoredMessageUnknownStatus), + iTimeOffset(0), + iDecodedOnSim(EFalse), + iForwardToClient(EFalse), + iForwardedCount(0), + iBitMap(NULL) + { + //Have to externalize so initialize to 0. + for (TInt i=0; iConstructL(aFileName); + CleanupStack::Pop(self); + + return self; + } + +void CPreallocatedFile::ConstructL(const TDesC& aFileName) + { + LOGSMSPROT1("CPreallocatedFile::ConstructL()"); + iFileName = aFileName.AllocL(); + } + +/** + * Constructor. +*/ +CPreallocatedFile::CPreallocatedFile(RFs& aFs, TInt aMaxClass0Msg, TInt aMaxPDUSeg, TPreAllocatedFileVersion aVersion) + :iFs(aFs), iEntryArray(KFlatArrayGranularity), iReinstateEntryInfo(KFlatArrayGranularity), iMaxClass0Msg(aMaxClass0Msg), iMaxPDUSeg(aMaxPDUSeg), iVersion(aVersion) + { + /* + Format of File: + Version Number, Header Section & Data Section + Version Number - Interger Value. + Header Section: Number of entries, Array of Entries, Array of PDU identifier section, Checksum. + Data Section: Array of Container. Each container contains Sms slot informatiuon, index number & PDU. + */ + + // Calculate the size of each section. + + // Entry section will always contain one more entry than configured one by the user. + // Because it will provide one extra slot to store the new message for time being + // before forwarding the oldest message. + iSizeOfEntrySection = (KSizeOfPreAllocatedStoreEntry * (iMaxClass0Msg + 1)); + + iSizeOfStorageIdentifierSection = ((sizeof(TInt))*(iMaxPDUSeg+1)); + + TInt sizeOfHeaderSection = KSizeOfNumberOfEntrySection + iSizeOfEntrySection + iSizeOfStorageIdentifierSection + KSizeOfChecksum; + + TInt sizeOfDataSection = KSizeOfAContainer * (iMaxPDUSeg+1); + + // Calculate the size of File. + iSizeOfFile = KSizeOfPreallocatedFileVersion + 2 * sizeOfHeaderSection + sizeOfDataSection; + + iBeginOfDuplicateHeaderSection = KSizeOfPreallocatedFileVersion + sizeOfHeaderSection; + iBeginOfDataSection = KSizeOfPreallocatedFileVersion + 2 * sizeOfHeaderSection; + } + +/** + * Destructor. It destroys all the member variables. +*/ +CPreallocatedFile::~CPreallocatedFile() + { + delete iFileName; + } + +/** +It checks & returns whether the file exist or not. If file is there whether +it is corrupted or not. + +@internalComponent +*/ +TBool CPreallocatedFile::IsFileOK() + { + LOGSMSPROT1("CPreallocatedFile::IsFileOK()"); + + TEntry entry; + // Check file exists + TInt ret=iFs.Entry(iFileName->Des(), entry); + // Check the size of file, if size does not match then assume that file + // is corrupted, then return KErrNotFound. + if ((ret == KErrNone) && (entry.iSize != iSizeOfFile)) + { + ret = KErrNotFound; + } + + if (ret == KErrNone) + { + return ETrue; + } + else + { + return EFalse; + } + } + +/** +It creates a pre-allocated file. + +@internalComponent +*/ +void CPreallocatedFile::CreateL() + { + LOGSMSPROT1("CPreallocatedFile::CreateL"); + + User::LeaveIfError(iFile.Replace(iFs, iFileName->Des(), EFileWrite)); + User::LeaveIfError(iFile.SetSize(iSizeOfFile)); + iFile.Flush(); + + // Externalize Version Number + //TInt version = iVersion; + TPtr8 memPtr((TUint8*) &iVersion, KSizeOfPreallocatedFileVersion, KSizeOfPreallocatedFileVersion); + iFile.Write(0, memPtr); + + //Externalize Header Information + ExternalizeEntryArray(); + + // Initialize Storage Identifier Section. + TInt storageIdentifier=0; + memPtr.Set((TUint8*) &storageIdentifier, sizeof(storageIdentifier), sizeof(storageIdentifier)); + TInt pos = KSizeOfNumberOfEntrySection + iSizeOfEntrySection; + TInt pos2 = 0; + for (TInt count=0; countDes(), EFileShareExclusive|EFileRead|EFileWrite)); + // Check the validity of the data. + CheckDataL(); + // Internalize data + InternalizeEntryArrayL(); + } + +/** +It closes the pre-allocated file. + +@internalComponent +*/ +void CPreallocatedFile::Close() + { + iFile.Close(); + iEntryArray.Reset(); + iReinstateEntryInfo.Reset(); + } + +/** +It internalizes the entry info from pre-allocated file. + +@internalComponent +*/ +void CPreallocatedFile::InternalizeEntryArrayL() + { + iEntryArray.Reset(); + TInt numberOfMessage; + TPtr8 memPtr((TUint8*) &numberOfMessage, sizeof(numberOfMessage), sizeof(numberOfMessage)); + iFile.Read(KBeginOfMasterHeaderSection, memPtr); + + TSmsPreAllocatedFileStoreReassemblyEntry tmpClass0ReassemblyStore; + memPtr.Set((TUint8*) &tmpClass0ReassemblyStore, KSizeOfPreAllocatedStoreEntry, KSizeOfPreAllocatedStoreEntry); + + TInt pos = 0; + for (TInt count=0; count < numberOfMessage; count++) + { + pos = sizeof(TInt) + (count * KSizeOfPreAllocatedStoreEntry); + iFile.Read(KBeginOfMasterHeaderSection + pos, memPtr); + iEntryArray.AppendL(tmpClass0ReassemblyStore); + } + } + +/** +It externalizes the entry info to pre-allocated file. + +@internalComponent +*/ +void CPreallocatedFile::ExternalizeEntryArray() + { + TInt count=iEntryArray.Count(); + TInt numberOfMessage=0; + TInt i=0; + + for (; i= aNumberOfPDUs) + { + break; + } + } + iFile.Flush(); + } + +/* +It updates the storage identifier section. It updates the corresponding container +in the storage identifier section specifying the PDU where it is stored. + +NOTE: + It keeps the record of added entries in iReinstateEntryInfo variable. + It will be required in case of revert operation. + After commiting the transaction, this varaible should be re-initialized. + +@param aIndex index number of the container where PDU has been stored.. +@param aStorageId unique id of added message. + +@internalComponent +*/ +void CPreallocatedFile::AddStorageIdL(TInt aIndex, TInt aStorageId) + { + //Put storage id in master header section + TPtr8 memPtr((TUint8*) &aStorageId, sizeof(aStorageId), sizeof(aStorageId)); + TInt beginOfStorageIdentifierSection = KBeginOfMasterHeaderSection + KSizeOfNumberOfEntrySection + iSizeOfEntrySection; + iFile.Write( beginOfStorageIdentifierSection + ((aIndex - 1) * sizeof(TInt)), memPtr); + + TReinstateEntryInfo entry; + entry.iContainerId=aIndex; + entry.iPreAllocatedStorageId=aStorageId; + entry.iFlag=EEntryIsAdded; + iReinstateEntryInfo.AppendL(entry); + } + +/* +It returns the next free available free container where next PDU can be stored. + +It goes through storage identifier section, it reads the conents of all storage +identfier & return that container which is free. +All the free container is identifiable by value 0. If the value is not 0 then it +contains the storage id of a particular message. + +@internalComponent +*/ +TInt CPreallocatedFile::GetFreeContainer() + { + // Get the next free slot. + TInt storageId; + TPtr8 memPtr((TUint8*) &storageId, sizeof(storageId), sizeof(storageId)); + TInt beginOfStorageIdSection = KBeginOfMasterHeaderSection + KSizeOfNumberOfEntrySection + iSizeOfEntrySection; + TInt beginOfStorageId = 0; + TInt count; + + for (count=0; count& aIndexArray, CArrayFix& aSmsArray) + { + LOGSMSPROT1("CPreallocatedFile::InternalizeEntryL"); + TSmsPreAllocatedFileStoreReassemblyEntry& entry = iEntryArray[aIndex]; + //Set other properties of CSmsMessage + aSmsMessage.SetStorage(entry.Storage()); + aSmsMessage.SetStatus((NMobileSmsStore::TMobileSmsStoreStatus)entry.Status()); + aSmsMessage.SetLogServerId(entry.LogServerId()); + aSmsMessage.SetTime(entry.Time()); + aSmsMessage.SetUTCOffset(entry.UTCOffset()); + aSmsMessage.SetDecodedOnSIM(entry.DecodedOnSIM()); + aSmsMessage.SetForwardToClient(entry.ForwardToClient()); + aSmsMessage.SetToFromAddressL(entry.Description2()); + + LOGSMSPROT2("CPreallocatedFile::InternalizeEntryL Start [sid=%d]", entry.PreAllocatedStorageId()); + if (entry.PreAllocatedStorageId()==KErrNotFound) + { + return; + } + + TInt beginOfStorageIdSection = KBeginOfMasterHeaderSection + KSizeOfNumberOfEntrySection + iSizeOfEntrySection; + //This will be used to read the storgae id which will indicate + //that corresponding container contains actual PDU + TInt storageId=0; + TPtr8 memPtr1((TUint8*) &storageId, sizeof(storageId), sizeof(storageId)); + + TInt storageIdPos = 0; + TInt dataPos = 0; + //This will be used to read the slot information + TGsmSmsSlotEntry smsSlotEntry; + TPtr8 memPtr2((TUint8*) &smsSlotEntry, KSizeOfGsmSmsSlotEntry, KSizeOfGsmSmsSlotEntry); + //This will be used to read the concatenated PDU index. + TInt index=0; + TPtr8 memPtr3((TUint8*) &index, sizeof(index), sizeof(index)); + //This will be used to read PDU. + RMobileSmsMessaging::TMobileSmsGsmTpdu pdu; + TPtr8 memPtr4((TUint8*) &pdu, KSizeOfSmsGsmPDU, KSizeOfSmsGsmPDU); + TGsmSms sms; + + //Number of PDUs stored in this pre-allocated file + TInt noOfPDUs = iEntryArray[aIndex].Count(); + //Will indicate how many number of PDUs already read from pre-allocated file. + TInt noOfPDUsRead = 0; + // Can also get the number of pdu stored in pre-allocated file from count's variable. + for (TInt count=0; count 256) || (aStartPos > aEndPos)) + { + User::Leave(KErrArgument); + } + + LOGSMSPROT2("CPreallocatedFile::RemovePDUsL Start [sid=%d]", iEntryArray[aIndex].PreAllocatedStorageId()); + if (iEntryArray[aIndex].PreAllocatedStorageId()==KErrNotFound) + { + return; + } + + TInt beginOfStorageIdSection = KBeginOfMasterHeaderSection + KSizeOfNumberOfEntrySection + iSizeOfEntrySection; + //This will be used to read the storgae id which will indicate + //that corresponding container contains actual PDU + TInt storageId=0; + TPtr8 memPtr1((TUint8*) &storageId, sizeof(storageId), sizeof(storageId)); + + //This will be used to cleanup the storage id. + TInt cleanStorageId=0; + TPtr8 memPtr2((TUint8*) &cleanStorageId, sizeof(cleanStorageId), sizeof(cleanStorageId)); + + //This will be used to read the concatenated PDU index. + TInt index=0; + TPtr8 memPtr3((TUint8*) &index, sizeof(index), sizeof(index)); + + //Number of PDUs stored in this pre-allocated file + TInt noOfPDUs = iEntryArray[aIndex].Count(); + //Will indicate how many number of PDUs already read from pre-allocated file. + TInt noOfPDUsRead = 0; + // Can also get the number of pdu stored in pre-allocated file from count's variable. + TInt noOfEntriesRemoved=0; + for (TInt count=0; count= aStartPos) && (index <= aEndPos)) + { + noOfEntriesRemoved++; + //Remove the PDU entry. + iFile.Write(storageIdPos, memPtr2); + } + + if (noOfPDUsRead == noOfPDUs) + { + break; + } + } + } + + /* + There are 3 scenarios in this case: + 1. If all the entries need to be removed, + then there is no need to store the entry in this permanent file. + 2. If few entries need to be removed, + then externalize the remaining entries. Update count field also. + 3. If no entries need to be removed, then do nothing. + */ + TSmsPreAllocatedFileStoreReassemblyEntry entry; + entry = iEntryArray[aIndex]; + + if (entry.Count() == noOfEntriesRemoved) + { + entry.SetPreAllocatedStorageId(KErrNotFound); + entry.SetCount(0); + ChangeEntryL(aIndex, entry); + } + else if (noOfEntriesRemoved > 0) + { + entry.SetCount(entry.Count() - noOfEntriesRemoved); + ChangeEntryL(aIndex, entry); + } + } + +/* +It stores the forwarded message info in forwarded pdu bit-map. + +@param aIndex index number of the forwarded message. +@param aStartPos starting pos of forwarded pdu. +@param aEndPos end pos of forwarded pdu. + +@internalComponent +*/ +void CPreallocatedFile::StoreForwardedPDUsInfoL(TInt aIndex, TInt aStartPos, TInt aEndPos) + { + TSmsPreAllocatedFileStoreReassemblyEntry entry; + entry = iEntryArray[aIndex]; + + TUint8 startBitMapIndex = (aStartPos-1)/8; + TUint8 endBitMapIndex = (aEndPos-1)/8; + TUint8 startBitPos = (aStartPos-1)%8; + TUint8 endBitPos = (aEndPos-1)%8; + + if (startBitMapIndex == endBitMapIndex) + { + TUint8 bitMap; + entry.GetBitMap(startBitMapIndex, bitMap); + TUint8 tmpBitMap = (0 | 0xFF); + tmpBitMap <<= ((8 - endBitPos) - 1); + tmpBitMap >>= (8 - ((endBitPos - startBitPos)+1)); + tmpBitMap <<= startBitPos; + bitMap |= tmpBitMap; + entry.SetBitMap(startBitMapIndex, bitMap); + } + else + { + TUint8 bitMap; + entry.GetBitMap(startBitMapIndex, bitMap); + TUint8 tmpBitMap = (0 | 0xFF); + tmpBitMap >>= (8 - ((7 - startBitPos)+1)); + tmpBitMap <<= startBitPos; + bitMap |= tmpBitMap; + entry.SetBitMap(startBitMapIndex, bitMap); + + entry.GetBitMap(endBitMapIndex, bitMap); + tmpBitMap = (0 | 0xFF); + tmpBitMap <<= ((8 - endBitPos) - 1); + tmpBitMap >>= (8 - ((endBitPos - 0)+1)); + tmpBitMap <<= 0; + bitMap |= tmpBitMap; + entry.SetBitMap(endBitMapIndex, bitMap); + } + + TInt noOfForwardedPDUs = (aEndPos - aStartPos) + 1; + entry.SetNumberOfPDUsForwardToClient(entry.NumberOfPDUsForwardToClient()+noOfForwardedPDUs); + + ChangeEntryL(aIndex, entry); + } + +/* +It checks the validity of master header section by comparing checksum value. +If the checksum value does not match, then it copies data from duplicate header section to +master header section. +*/ +void CPreallocatedFile::CheckDataL() + { + TUint32 checksum; + TInt checksumPos = KBeginOfMasterHeaderSection + KSizeOfNumberOfEntrySection + iSizeOfEntrySection + iSizeOfStorageIdentifierSection; + + TPtr8 memPtr((TUint8*) &checksum, sizeof(checksum), sizeof(checksum)); + iFile.Read(checksumPos, memPtr); + + if (ChecksumValue() != checksum) + { + CopyDuplicateToMasterL(); + } + } + +/* +It writes the checksum value & then copies header information from master section to duplicate section. +*/ +void CPreallocatedFile::PutChecksumValueL() + { + TUint32 checksum = ChecksumValue(); + TInt checksumPos = KBeginOfMasterHeaderSection + KSizeOfNumberOfEntrySection + iSizeOfEntrySection + iSizeOfStorageIdentifierSection; + + TPtr8 memPtr((TUint8*) &checksum, sizeof(checksum), sizeof(checksum)); + iFile.Write(checksumPos, memPtr); + iFile.Flush(); + CopyMasterToDuplicateL(); + } + +/* +It calculates & returns the checksum value of master header section. +*/ +TUint32 CPreallocatedFile::ChecksumValue() + { + TInt sizeOfDataToBeChecksum = KSizeOfNumberOfEntrySection + iSizeOfEntrySection + iSizeOfStorageIdentifierSection; + + TUint32 checksum(0); + + TUint32 tmpValue; + TPtr8 memPtr((TUint8*) &tmpValue, sizeof(tmpValue), sizeof(tmpValue)); + + for (TInt i = 0; i < sizeOfDataToBeChecksum/4 ; i++) + { + iFile.Read(KBeginOfMasterHeaderSection + (i*4), memPtr); + checksum += tmpValue; + } + return checksum; + } + +/* +It copies header information from master copy to duplicate copy. +*/ +void CPreallocatedFile::CopyMasterToDuplicateL() + { + TInt sizeOfDataToBeCopied = KSizeOfNumberOfEntrySection + iSizeOfEntrySection + iSizeOfStorageIdentifierSection + 4; + + HBufC8* heapBuffer = HBufC8::NewL(sizeOfDataToBeCopied); + CleanupStack::PushL(heapBuffer); + TPtr8 memPtr(heapBuffer->Des()); + iFile.Read(KBeginOfMasterHeaderSection, memPtr); + iFile.Write(iBeginOfDuplicateHeaderSection, memPtr); + iFile.Flush(); + CleanupStack::PopAndDestroy(heapBuffer); + } + +/* +It copies header information from duplicate copy to master copy. +*/ +void CPreallocatedFile::CopyDuplicateToMasterL() + { + TInt sizeOfDataToBeCopied = KSizeOfNumberOfEntrySection + iSizeOfEntrySection + iSizeOfStorageIdentifierSection + 4; + + HBufC8* heapBuffer = HBufC8::NewL(sizeOfDataToBeCopied); + CleanupStack::PushL(heapBuffer); + TPtr8 memPtr(heapBuffer->Des()); + iFile.Read(iBeginOfDuplicateHeaderSection, memPtr); + iFile.Write(KBeginOfMasterHeaderSection, memPtr); + iFile.Flush(); + CleanupStack::PopAndDestroy(heapBuffer); + } + +/* +It returns the total number of PDUs stored in pre-allocated file. +*/ +TInt CPreallocatedFile::NumberOfPDUStored() + { + TInt noOfPDUs = 0; + for (TInt i = 0; i < iEntryArray.Count(); i++) + { + noOfPDUs += iEntryArray[i].Count(); + } + return noOfPDUs; + } + +/** + * Sets the pre-allocated file store as in-transaction. + * + * The function checks the validity of the call and leaves KErrAccessDenied if + * invalid. + * @capability None + */ +void CPreallocatedFile::BeginTransactionL() + { + LOGSMSPROT3("CPreallocatedFile::BeginTransactionL [this=0x%08X iInTransaction=%d]", this, iInTransaction); + + if (iInTransaction) + { + LOGGSMU1("WARNING CPreallocatedFile::BeginTransactionL leaving with KErrAccessDenied"); + User::Leave(KErrAccessDenied); + } + + iInTransaction = ETrue; + } + +/** + * It commits the transaction. + */ +void CPreallocatedFile::CommitTransactionL() + { + LOGSMSPROT3("CPreallocatedFile::CommitTransactionL(): this=0x%08X iInTransaction=%d", + this, iInTransaction); + + ExternalizeEntryArray(); + //Commit + PutChecksumValueL(); + iInTransaction = EFalse; + iReinstateEntryInfo.Reset(); + RemoveDeletedEntries(); + } + +/* +It reverts the transaction. +*/ +void CPreallocatedFile::Revert() + { + LOGSMSPROT3("CPreallocatedFile::Revert(): this=0x%08X, iInTransaction=%d", + this, iInTransaction); + + ReinstateEntries(); + ExternalizeEntryArray(); + iInTransaction = EFalse; + ReinstateDeletedEntries(); + } + +/** + * It removes the deleted entries from entry arry. + * This function is called after commit. + */ +void CPreallocatedFile::RemoveDeletedEntries() + { + LOGSMSPROT1("CPreallocatedFile::RemoveDeletedEntries()"); + + TInt count=iEntryArray.Count(); + while (count--) + { + TSmsPreAllocatedFileStoreReassemblyEntry& entry = iEntryArray[count]; + + if (entry.IsDeleted()) + { + iEntryArray.Delete(count); + } + else + { + entry.SetIsAdded(EFalse); + } + } + } // CPreallocatedFile::RemoveDeletedEntries + +/** + * It reinstate the deleted/added entries from entry arry. + * This function is called after revert operation. + */ +void CPreallocatedFile::ReinstateDeletedEntries() + { + LOGSMSPROT1("CPreallocatedFile::ReinstateDeletedEntries()"); + + TInt count=iEntryArray.Count(); + while (count--) + { + TSmsPreAllocatedFileStoreReassemblyEntry& entry = iEntryArray[count]; + + if (entry.IsAdded()) + { + iEntryArray.Delete(count); + } + else + { + entry.SetIsDeleted(EFalse); + } + } + } // CPreallocatedFile::ReinstateDeletedEntries + +/** + * It reinstate the deleted/added entries from the container. + * This function is called at the time of revert operation. + * Unlike permanent store file which supports revert. pre-allocated + * file (raw data file) supports the revert mecahnism using this function. + */ +void CPreallocatedFile::ReinstateEntries() + { + LOGSMSPROT1("CPreallocatedFile::ReinstateEntries()"); + + TInt containerId; + TInt storageId; + TPtr8 memPtr((TUint8*) &storageId, sizeof(storageId), sizeof(storageId)); + + TInt count = iReinstateEntryInfo.Count(); + + for (TInt i=0; i0)) + { + time = iEntryArray[index2].Time(); + index = index2; + } + } + return index; + } + +/** +Static factory constructor. Uses two phase +construction and leaves nothing on the CleanupStack. + +@param aClass0ReassemblyStore reference to class 0 reasembly store. +@param aGuardTimeout guard time out in hours. + +@return A pointer to the newly created CGuardTimer object. + +@internalComponent +*/ +CGuardTimer* CGuardTimer::NewL(CClass0SmsReassemblyStore& aClass0ReassemblyStore, TInt aGuardTimeout) + { + LOGSMSPROT1("CGuardTimer::NewL()"); + + CGuardTimer* timer = new(ELeave) CGuardTimer(aClass0ReassemblyStore, aGuardTimeout); + CleanupStack::PushL(timer); + timer->ConstructL(); + CleanupStack::Pop(); + return timer; + } // CGuardTimer::NewL + +/** + * Constructor + */ +CGuardTimer::CGuardTimer(CClass0SmsReassemblyStore& aClass0ReassemblyStore, TInt aGuardTimeout) + :CTimer(KSmsSessionPriority), + iClass0ReassemblyStore(aClass0ReassemblyStore), + iTimeoutInSecs(aGuardTimeout*3600) + { + CActiveScheduler::Add(this); + } //CGuardTimer::CGuardTimer + +/** + * Destructor + */ +CGuardTimer::~CGuardTimer() + { + Cancel(); + } // CGuardTimer::~CGuardTimer + +/** + * Enable the Gurad Timer + */ +void CGuardTimer::EnableGuardTimer() + { + LOGSMSPROT1("CGuardTimer::EnableGuardTimer()"); + if (!IsActive()) + { + TTime nextTimeOut; + iClass0ReassemblyStore.GetNextGuardTimeout(nextTimeOut); + At(nextTimeOut); + } + } //CGuardTimer::EnableGuardTimer + +/** + * Disable the Gurad Timer + */ +void CGuardTimer::DisableGuardTimer() + { + Cancel(); + } + +/** + * Timer completed + */ +void CGuardTimer::RunL() + { + LOGSMSPROT2("CGuardTimer::RunL [iStatus=%d]", iStatus.Int()); + iClass0ReassemblyStore.ProcessTimeoutMessageL(); + EnableGuardTimer(); + } // CGuardTimer::RunL + +/** +Static factory constructor. Uses two phase +construction and leaves nothing on the CleanupStack. + +@param aFs File Server handle. +@param aSmsComm reference to MSmsComm event handler. + +@return A pointer to the newly created CClass0SmsReassemblyStore object. + +@pre A connected file server session must be passed as parameter. +@post CClass0SmsReassemblyStore object is now fully initialised + +@internalComponent +*/ +CClass0SmsReassemblyStore* CClass0SmsReassemblyStore::NewL(RFs& aFs, MSmsComm& aSmsComm) + { + LOGSMSPROT1("CClass0SmsReassemblyStore::NewL()"); + + CClass0SmsReassemblyStore* self = new (ELeave) CClass0SmsReassemblyStore(aFs, aSmsComm); + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(self); + + return self; + } + +/** + * C'tor + */ +CClass0SmsReassemblyStore::CClass0SmsReassemblyStore(RFs& aFs, MSmsComm& aSmsComm) + :CReassemblyStore(aFs), iSmsComm(aSmsComm), iPermanentFileStore(NULL), iPreallocatedFile(NULL), + iGuardTimer(NULL), iInTransaction(EFalse), iDiskSpaceStatus(ESmsDiskSpaceAvailable) + { + } + +/** + * D'tor + */ +CClass0SmsReassemblyStore::~CClass0SmsReassemblyStore() + { + delete iPermanentFileStore; + delete iPreallocatedFile; + delete iGuardTimer; + } + +/* +It constructs permanent and pre-allocated file to store the class 0 messages. +It creates a guard timer so that messages can be deleted if all the constituent +PDU of a message is not received within configured time-frame. +It also reserves 4KB disk space so that reserved space can be used to +delete a stream from permanent file store in out-of-disk condition. + +@internalComponent +*/ +void CClass0SmsReassemblyStore::ConstructL() + { + //Reserve Drive Space + User::LeaveIfError(iFs.ReserveDriveSpace(KStoreDrive, 4*1024)); + + //Read Configuration file + ReadConfigurableClass0SmsSettingsL(iMaxClass0Msg, iMaxPDUSeg, iGuardTimeOut); + //Create Permanent store file + TFileName permanentStoreFileName; + CReassemblyStoreUtility::PrivatePath(iFs, permanentStoreFileName); + permanentStoreFileName.Append(KClass0ReassemblyStoreName); + iPermanentFileStore = CSmsPermanentFileStore::NewL(iFs, permanentStoreFileName, KClass0ReassemblyStoreUid); + //Create Pre-allocated file + TFileName preAllocatedFileName; + CReassemblyStoreUtility::PrivatePath(iFs, preAllocatedFileName); + preAllocatedFileName.Append(KPreAllocatedFileName); + iPreallocatedFile = CPreallocatedFile::NewL(iFs, preAllocatedFileName, iMaxClass0Msg, iMaxPDUSeg); + //Create Gurad Timer + iGuardTimer = CGuardTimer::NewL(*this, iGuardTimeOut); + } + +/* +It reads class 0 re-assembly store configuration information from smswap.sms.esk file. +It reads the following information: MaxClass0Messages, NumberOfPDUSegements, GuardTimeOut. +If any of the above element is missing it takes the default value. + +@internalComponent +*/ +void CClass0SmsReassemblyStore::ReadConfigurableClass0SmsSettingsL(TInt& aMaxClass0Msg, TInt& aMaxPDUSeg, TInt& aGuardTimeOut) + { + LOGSMSPROT1("CClass0SmsReassemblyStore::ReadConfigurableClass0SmsSettingsL()"); + + aMaxClass0Msg = KMaxNumberOfClass0MessagesInReassemblyStore; + aMaxPDUSeg = KNumberOfPDUSegmentsStoredInOODCondition; + aGuardTimeOut = KGuardTimeOut; + + CESockIniData* ini = NULL; + TRAPD(ret, ini=CESockIniData::NewL(_L("smswap.sms.esk"))); + if(ret!=KErrNone) + { + LOGSMSPROT2("CESockIniData::NewL() returned=%d", ret); + } + else + { + CleanupStack::PushL(ini); + + TInt var(0); + if(ini->FindVar(_L("ReassemblyStore"),_L("MaxClass0Messages"),var)) + { + if (var > 0) + { + LOGSMSPROT2("MaxClass0Messages [%d]", var); + aMaxClass0Msg = var; + } + } + + if(ini->FindVar(_L("ReassemblyStore"),_L("NumberOfPDUSegements"),var)) + { + if (var > 0) + { + LOGSMSPROT2("MaxClass0Messages [%d]", var); + aMaxPDUSeg = var; + } + } + + if(ini->FindVar(_L("ReassemblyStore"),_L("GuardTimeOut"),var)) + { + if (var > 0) + { + LOGSMSPROT2("MaxClass0Messages [%d]", var); + aGuardTimeOut = var; + } + } + + CleanupStack::PopAndDestroy(ini); + } + + LOGSMSPROT4("CClass0SmsReassemblyStore::ReadConfigurableClass0SmsSettingsL(): aMaxClass0Msg=%d, aMaxPDUSeg=%d, aGuardTimeOut=%d", + aMaxClass0Msg, aMaxPDUSeg, aGuardTimeOut); + } + +/** +It opens the class 0 re-assembly store. +Then it populates the entry information (all the messages stored in re-assembly store). + +@internalComponent +*/ +void CClass0SmsReassemblyStore::OpenStoreL() + { + LOGSMSPROT1("CClass0SmsReassemblyStore::OpenStoreL()"); + TFileName pathName; + CReassemblyStoreUtility::PrivatePath(iFs, pathName); + //Create the directory if it is not created. + iFs.MkDirAll(pathName); + // If any one file becomes corrupt or does not exist then we have to create both files. + if (iPreallocatedFile->IsFileOK() && iPermanentFileStore->IsFileOK()) + { + iPreallocatedFile->OpenL(); + iPermanentFileStore->OpenL(); + } + else + { + iPreallocatedFile->CreateL(); + iPermanentFileStore->CreateL(); + } + PopulateEntryArrayL(iEntryArray); + iGuardTimer->EnableGuardTimer(); + } + +/** +It closes the class 0 re-assembly store. + +@internalComponent +*/ +void CClass0SmsReassemblyStore::Close() + { + LOGSMSPROT1("CClass0SmsReassemblyStore::CloseStore()"); + iGuardTimer->DisableGuardTimer(); + iEntryArray.Reset(); + iPreallocatedFile->Close(); + iPermanentFileStore->Close(); + } + +/** +It populates the entry information stored in class 0 reassembly store. + +@param aEntryArray reference to entry array. + +@internalComponent +*/ +void CClass0SmsReassemblyStore::PopulateEntryArrayL(CArrayFix& aEntryArray) + { + LOGSMSPROT1("CClass0SmsReassemblyStore::PopulateEntryArrayL()"); + aEntryArray.Reset(); + //Populate Entries from Pre-allocated file. + for (TInt count = 0; count < iPreallocatedFile->Entries().Count(); count++) + { + TReassemblyEntry entry; + entry.SetReference(iPreallocatedFile->Entries()[count].Reference()); + entry.SetTotal(iPreallocatedFile->Entries()[count].Total()); + entry.SetCount(iPreallocatedFile->Entries()[count].Count()+iPreallocatedFile->Entries()[count].NumberOfPDUsForwardToClient()); + entry.SetLogServerId(iPreallocatedFile->Entries()[count].LogServerId()); + + // Check that descriptor2 (sms address )is not corrupted + TPtrC ptr = iPreallocatedFile->Entries()[count].Description2(); + + if (ptr.Length() <= CSmsAddress::KSmsAddressMaxAddressLength) + { + entry.SetDescription2(iPreallocatedFile->Entries()[count].Description2()); + } + + entry.SetPduType(iPreallocatedFile->Entries()[count].PduType()); + entry.SetStorage(iPreallocatedFile->Entries()[count].Storage()); + entry.SetTime(iPreallocatedFile->Entries()[count].Time()); + entry.SetPassedToClient(iPreallocatedFile->Entries()[count].PassedToClient()); + aEntryArray.AppendL(entry); + } + + /* + Then populate the entries from permanent store file. It is needed + because permanent store file might contain few mesages. + But before populating the entry information it is needed to cleanup + the entries in permanent store file. It is needed to ensure that permanent file + clean itself with pre-allocated file. There migth be a scenario where + user has deleted a message but the corresponding message has not been + deleted from permanent store file due to out-of-disk condition. + And also at the time of forwarding an incomplete message a forwarded + message has not been deleted due to above reason. But the entry/PDU is + invalid because it is no more in the pre-allocated file which + contains master header info. + */ + TInt ret = CleanReassemblyEntries(); + + if (ret == KErrNone) + { + /* + In this case permanent store file contains correct information. + So populate Entry information from Permanent store file. + In this case only count information needs to be updated. + */ + TInt permanentFileStoreIndex; + for (TInt i=0; iMatchEntryToExistingMessage(aEntryArray[i], permanentFileStoreIndex); + if (permanentFileStoreIndex!=KErrNotFound) + { + aEntryArray[i].SetCount(aEntryArray[i].Count() + iPermanentFileStore->Entries()[permanentFileStoreIndex].Count()); + if (aEntryArray[i].Count() > aEntryArray[i].Total()) + { + aEntryArray[i].SetCount(aEntryArray[i].Total()); + } + } + } + } + else if (ret == KErrDiskFull) + { + LOGSMSPROT1("CleanReassemblyEntries() returns KErrDiskFull"); + /* + In this case permanent store file contains incorrect information. + For example forwarded message might be still stored in this store. + Because it has not been deleted due to out-of-disk condition. + So be careful at the time of updating the count information + about the message. + */ + TInt permanentFileStoreIndex; + for (TInt i=0; iMatchEntryToExistingMessage(aEntryArray[i], permanentFileStoreIndex); + if (permanentFileStoreIndex!=KErrNotFound) + { + TSmsPreAllocatedFileStoreReassemblyEntry entry = iPreallocatedFile->Entries()[i]; + if (entry.NumberOfPDUsForwardToClient() > 0) + { + //Internalize the entries. + CSmsBuffer* buffer = CSmsBuffer::NewL(); + CSmsMessage* smsMessage = CSmsMessage::NewL(iFs, CSmsPDU::ESmsDeliver, buffer); + CleanupStack::PushL(smsMessage); + + CArrayFix* indexArray = new(ELeave) CArrayFixFlat(KFlatArrayGranularity); + CleanupStack::PushL(indexArray); + CArrayFixFlat* smsArray = new(ELeave) CArrayFixFlat(KFlatArrayGranularity); + CleanupStack::PushL(smsArray); + //Internalize to check whether removed PDUs still stored in Permanent store file. + iPermanentFileStore->InternalizeEntryL(permanentFileStoreIndex, *smsMessage, *indexArray, *smsArray); + TInt noOfForwardedEntries=0; + for (TInt j=0; jCount() ;j++) + { + TUint8 bitMapIndex = (indexArray->At(j)-1)/8; + TUint8 bitPos = (indexArray->At(j)-1)%8; + TUint8 bitMap; + entry.GetBitMap(bitMapIndex, bitMap); + TUint8 tmpBitMap = 1; + tmpBitMap <<= bitPos; + if (tmpBitMap == (bitMap & tmpBitMap)) + { + noOfForwardedEntries++; + } + } + TInt count = iPermanentFileStore->Entries()[permanentFileStoreIndex].Count() - noOfForwardedEntries; + aEntryArray[i].SetCount(aEntryArray[i].Count() + count); + + CleanupStack::PopAndDestroy(3, smsMessage); // smsMessage, indexArray, smsArray + } + else + { + aEntryArray[i].SetCount(aEntryArray[i].Count() + iPermanentFileStore->Entries()[permanentFileStoreIndex].Count()); + } + } + } + } + else + { + User::Leave(ret); + } + } + +/** +It sets the disk space status. +If disk space is full, then class 0 re-assembly store stores the incoming message in +pre-allocated file. Otherwise it stores the message in permanent store file. +*/ +void CClass0SmsReassemblyStore::SetDiskSpaceState(TSmsDiskSpaceMonitorStatus aDiskSpaceStatus) + { + LOGSMSPROT1("CClass0SmsReassemblyStore::SetDiskSpaceState()"); + iDiskSpaceStatus = aDiskSpaceStatus; + } + +/* +It adds the new message in class 0 reassembly store. +It first tries to add the message in permanent store file. If it is +successful, then it adds the entry information in pre-allocated file. +Because pre-allocated file contains master header entry information. +Otherwise (if disk space is full), it adds the message in pre-allocated file. + +@param aSmsMessage reference to sms message to be added. +@param aGsmSms reference to GsmSms object to be added. + +@internalComponent +*/ +void CClass0SmsReassemblyStore::AddNewMessageL(CSmsMessage& aSmsMessage,const TGsmSms& aGsmSms) + { + LOGSMSPROT1("CClass0SmsReassemblyStore::AddNewMessageL"); + + // Add entry in permanent store file + TInt index; + TInt ret = KErrNone; + + if (iDiskSpaceStatus == ESmsDiskSpaceFull) + { + ret = KErrDiskFull; + } + else + { + TRAP(ret, iPermanentFileStore->AddNewMessageL(index, aSmsMessage, aGsmSms)); + } + + if (ret == KErrNone) + { + // Add a new entry in pre-allocated file + TSmsPreAllocatedFileStoreReassemblyEntry tmpEntry; + //Fill-up entry information... + CReassemblyStoreUtility::PopulateEntry(tmpEntry, aSmsMessage, 1); + + //The properties below need to be set only once when a PDU of a new message arrives. + tmpEntry.SetStatus((RMobileSmsStore::TMobileSmsStoreStatus)aSmsMessage.Status()); + tmpEntry.SetUTCOffset(aSmsMessage.UTCOffset()); + tmpEntry.SetDecodedOnSIM(aSmsMessage.DecodedOnSim()); + tmpEntry.SetForwardToClient(aSmsMessage.ForwardToClient()); + //Set KErrNotFound because message corresponding to + //this entry is not in this pr-allocated file. + tmpEntry.SetPreAllocatedStorageId(KErrNotFound); + //Has to set 0 because PDU is stored in Permanent store file instead of pre-allocated file. + tmpEntry.SetCount(0); + + iPreallocatedFile->AddEntryL(tmpEntry); + } + else if (ret == KErrDiskFull) + { + // Add the new message in pre-allocated file + iPreallocatedFile->AddNewMessageL(index, aSmsMessage, aGsmSms); + } + else + { + User::Leave(ret); + } + } + +/* +It updates the existing message in class 0 re-assembly store. + +@param aSmsMessage reference to sms message to be updated. +@param aGsmSms reference to GsmSms object to be added. +@param aDuplicateMsgRef boolean value (output) indicates whether the added PDU is a duplicate or not. +@param aDuplicateSlot boolean value (output) indicates whether the added PDU is a duplicate enumerated PDU. + +@internalComponent +*/ +void CClass0SmsReassemblyStore::UpdateExistingMessageL(CSmsMessage& aSmsMessage, const TGsmSms& aGsmSms, TBool& aDuplicateMsgRef, TBool& aDuplicateSlot) + { + LOGSMSPROT1("CClass0SmsReassemblyStore::UpdateExistingMessageL()"); + + aDuplicateMsgRef = EFalse; + aDuplicateSlot = EFalse; + + CSmsBuffer* buffer = CSmsBuffer::NewL(); + CSmsMessage* smsMessage = CSmsMessage::NewL(iFs, CSmsPDU::ESmsDeliver, buffer); + CleanupStack::PushL(smsMessage); + + CArrayFix* indexArray = new(ELeave) CArrayFixFlat(KFlatArrayGranularity); + CleanupStack::PushL(indexArray); + CArrayFixFlat* smsArray = new(ELeave) CArrayFixFlat(KFlatArrayGranularity); + CleanupStack::PushL(smsArray); + + //Internalize stored message in permanent store file + TReassemblyEntry entry; + CReassemblyStoreUtility::PopulateEntry(entry, aSmsMessage, 1); + TInt permanentStoreIndex; + iPermanentFileStore->MatchEntryToExistingMessage(entry, permanentStoreIndex); + if (permanentStoreIndex!=KErrNotFound) + { + iPermanentFileStore->InternalizeEntryL(permanentStoreIndex, *smsMessage, *indexArray, *smsArray); + } + + //Internalize stored message in pre-allocated file + TInt preAllocatedFileIndex; + iPreallocatedFile->MatchEntryToExistingMessage(entry, preAllocatedFileIndex); + if (preAllocatedFileIndex!=KErrNotFound) + { + iPreallocatedFile->InternalizeEntryL(preAllocatedFileIndex, *smsMessage, *indexArray, *smsArray); + } + else + { + //This condition should never arise + User::Leave(KErrNotFound); + } + + // + // Check if this is a duplicated enumerated PDU (e.g. on the SIM or phone memory) + // or a duplicated PDU (e.g. in the Reassembly Store)... + // + TInt concatPDUIndex = aSmsMessage.SmsPDU().ConcatenatedMessagePDUIndex(); + + if (smsMessage->Storage() == CSmsMessage::ESmsSIMStorage || + smsMessage->Storage() == CSmsMessage::ESmsCombinedStorage) + { + // + // In most cases this PDU is being enumerated, but not always. It is + // possible for the PDU to be stored on the SIM first before it is + // received. + // + const TGsmSmsSlotEntry& newSlot = aSmsMessage.iSlotArray[0]; + TInt slotArrayCount = smsMessage->iSlotArray.Count(); + + for (TInt slotNum = 0; slotNum < slotArrayCount; slotNum++ ) + { + const TGsmSmsSlotEntry& slot = smsMessage->iSlotArray[slotNum]; + + if (slot.iIndex == newSlot.iIndex && slot.iStore == newSlot.iStore) + { + LOGSMSPROT1("CSmsReassemblyStore::UpdateExistingMessageL(): Duplicate enumerated PDU."); + + // It is a duplicate that was already stored on the SIM... + aDuplicateSlot = ETrue; + break; + } + } + } + + TInt indexArrayCount = indexArray->Count(); + + for (TInt index = 0; index < indexArrayCount; index++ ) + { + if (indexArray->At(index) == concatPDUIndex) + { + LOGSMSPROT1("CSmsReassemblyStore::UpdateExistingMessageL(): Duplicate concatenated PDU."); + + // The PDU is already stored in the reassembly store. + aDuplicateMsgRef = ETrue; + break; + } + } + + //Check whether this PDU is part of forwarded PDU. + TSmsPreAllocatedFileStoreReassemblyEntry preAllocatedFileEntry = iPreallocatedFile->Entries()[preAllocatedFileIndex]; + if (preAllocatedFileEntry.NumberOfPDUsForwardToClient() > 0) + { + TUint8 bitMapIndex = (concatPDUIndex-1)/8; + TUint8 bitPos = (concatPDUIndex-1)%8; + TUint8 bitMap; + preAllocatedFileEntry.GetBitMap(bitMapIndex, bitMap); + TUint8 tmpBitMap = 1; + tmpBitMap <<= bitPos; + if (tmpBitMap == (bitMap & tmpBitMap)) + { + aDuplicateMsgRef = ETrue; + } + } + + if (aDuplicateMsgRef || aDuplicateSlot) + { + CleanupStack::PopAndDestroy(3, smsMessage); // smsMessage, smsArray, indexArray + return; + } + + // + // If the PDU is stored then add the slot information... + // + if (aSmsMessage.Storage() == CSmsMessage::ESmsSIMStorage || + aSmsMessage.Storage() == CSmsMessage::ESmsCombinedStorage) + { + smsMessage->AddSlotL(aSmsMessage.iSlotArray[0]); + } + + // + // If the PDU is Unsent or Unread, then store that information... + // + NMobileSmsStore::TMobileSmsStoreStatus status = aSmsMessage.Status(); + + if (status == NMobileSmsStore::EStoredMessageUnsent || + status == NMobileSmsStore::EStoredMessageUnread) + { + smsMessage->SetStatus(status); + } + + /* + Below logic is written in keeping view of different scenarios that might occur here: + (1) Previous PDUs are in Permanent store file, this PDU arrives in out-of-disk condition (so will be stored in Pre-allocated file). + (2) Previous PDUs are in Pre-allocated file (means PDU arrives in out-of-disk condition), this PDU arrives in normal condition. + (3) Previous PDUs are in Permanent store file, this PDU also arrives in normal condition. + (4) Previous PDUs are in Pre-allocated file (means PDU arrives in out-of-disk condition), this PDU also arrives in out-of-disk condition. + */ + + //Always first try to store the message in permanent store file. + //In case of out-of-disk condition store the message in pre-allocated file + TInt ret = KErrNone; + if (iDiskSpaceStatus == ESmsDiskSpaceFull) + { + ret = KErrDiskFull; + } + else + { + if (permanentStoreIndex==KErrNotFound) + { + // See condition (2) + TInt tmpIndex; + TRAP(ret, iPermanentFileStore->AddNewMessageL(tmpIndex, aSmsMessage, aGsmSms)); + } + else + { + CSmsBuffer* tmpBuffer = CSmsBuffer::NewL(); + CSmsMessage* tmpSmsMessage = CSmsMessage::NewL(iFs, CSmsPDU::ESmsDeliver, tmpBuffer); + CleanupStack::PushL(tmpSmsMessage); + + indexArray->Reset(); + smsArray->Reset(); + iPermanentFileStore->InternalizeEntryL(permanentStoreIndex, *tmpSmsMessage, *indexArray, *smsArray); + /* + Check & remove those PDU from permanent store file which has already been + forwarded to client. Forwarded PDU might still remain in permanent store + file if it is deleted at the time of out-of-disk condition. + */ + if (preAllocatedFileEntry.NumberOfPDUsForwardToClient()>0) + { + for (TInt j=indexArray->Count(); j>0 ;j--) + { + TUint8 bitMapIndex = (indexArray->At(j-1)-1)/8; + TUint8 bitPos = (indexArray->At(j-1)-1)%8; + TUint8 bitMap; + preAllocatedFileEntry.GetBitMap(bitMapIndex, bitMap); + TUint8 tmpBitMap = 1; + tmpBitMap <<= bitPos; + if (tmpBitMap == (bitMap & tmpBitMap)) + { + indexArray->Delete(j-1); + smsArray->Delete(j-1); + } + } + } + + indexArray->AppendL(concatPDUIndex); + smsArray->AppendL(aGsmSms); + + TRAP(ret, iPermanentFileStore->UpdateExistingMessageL(permanentStoreIndex, *smsMessage, *indexArray, *smsArray)); + + CleanupStack::PopAndDestroy(1); //tmpSmsMessage + } + } + + if (ret == KErrDiskFull) + { + iPreallocatedFile->UpdateExistingMessageL(preAllocatedFileIndex, aSmsMessage, concatPDUIndex, aGsmSms); + } + else if (ret!=KErrNone) + { + User::Leave(ret); + } + + CleanupStack::PopAndDestroy(3, smsMessage); // smsMessage, indexArray, smsArray + } + +/* +It retrieves class 0 message from re-assembly store. +The message which match the passed entry information is returned from this function. + +@param aEntry reference to entry information. +@param aSmsMessage reference to returned sms message. + +@internalComponent +*/ +void CClass0SmsReassemblyStore::RetrieveMessageL(const TReassemblyEntry& aEntry, CSmsMessage& aSmsMessage) + { + CArrayFix* indexArray = new(ELeave) CArrayFixFlat(KFlatArrayGranularity); + CleanupStack::PushL(indexArray); + CArrayFixFlat* smsArray = new(ELeave) CArrayFixFlat(KFlatArrayGranularity); + CleanupStack::PushL(smsArray); + + GetSmsEntriesL(aEntry, aSmsMessage, *indexArray, *smsArray); + + //There is no need to decode a single segment message + if (aEntry.Total()>1) + { + if (smsArray->Count() == aEntry.Total()) + { + aSmsMessage.DecodeMessagePDUsL(*smsArray); + } + } + CleanupStack::PopAndDestroy(2, indexArray); //smsArray, indexArray + } + +/* +It deletes the entry from re-assembly store. + +@param aEntry reference to entry information. + +@internalComponent +*/ +void CClass0SmsReassemblyStore::DeleteEntryL(const TReassemblyEntry& aEntry) + { + TInt index; + iPreallocatedFile->MatchEntryToExistingMessage(aEntry, index); + if (index == KErrNotFound) + { + //This condition should never arise + User::Leave(KErrNotFound); + } + iPreallocatedFile->DeleteEntryL(index); + iPermanentFileStore->MatchEntryToExistingMessage(aEntry, index); + if (index != KErrNotFound) + { + TRAP_IGNORE(iPermanentFileStore->DeleteEntryL(index)); + } + } + +/* +It updates the log server id of the passed entry (message). + +@param aEntry reference to entry information. +@param aLogId log id of the message to be updated. + +@internalComponent +*/ +void CClass0SmsReassemblyStore::UpdateLogServerIdL(const TReassemblyEntry& aEntry, TLogId aLogServerId) + { + TInt index; + iPreallocatedFile->MatchEntryToExistingMessage(aEntry, index); + if (index == KErrNotFound) + { + //This condition should never arise + User::Leave(KErrNotFound); + } + iPreallocatedFile->UpdateLogServerIdL(index, aLogServerId); + } + +/* +It sets the message passed to client or not. The message, which match the +passed entry information, its passed to client value is set. + +@param aEntry reference to entry information. +@param aBool boolean value indicating whether message is passed or not. + +@internalComponent +*/ +void CClass0SmsReassemblyStore::SetPassedToClientL(const TReassemblyEntry& aEntry, TBool aBool) + { + TInt index; + iPreallocatedFile->MatchEntryToExistingMessage(aEntry, index); + if (index == KErrNotFound) + { + //This condition should never arise + User::Leave(KErrNotFound); + } + iPreallocatedFile->SetPassedToClientL(index, aBool); + } + +/** +It forwards the complete class 0 messages to client. +It checks whether the received object is complete or not. +If it is complete, then it forwards the complete message to client. +Otherwise it forms the message & forward the message to client. +In this it might be possible to forward multiple incomplete message. + +@param aSmsComm a reference to aSmsComm object which implemented the events. + +@param aSmsMessage a reference to sms message object. This sms message must be class 0 messages. + +@param aOriginalSmsAddr pointer to the address of the sender of a previously sent + +@param aOriginalSmsMessage pointer to a message previously sent matched to the received + one (e.g. status report). Null if not matched. + +@param aDes user data for the deliver report acknowledging this message to the SC. + Filled in by the observer. + +@internalComponent +*/ +void CClass0SmsReassemblyStore::ForwardCompleteClass0SmsMessagesL(MSmsComm& aSmsComm, const CSmsMessage& aSmsMessage,const TSmsAddr* aOriginalSmsAddr,const CSmsMessage* aOriginalSmsMessage,TDes& aDes) + { + TBool passedToClient=ETrue; + + if (aSmsMessage.IsComplete()) + { + //Message is complete so forward it. + TInt ret = aSmsComm.ProcessMessageL(aSmsMessage, aOriginalSmsAddr, aOriginalSmsMessage, aDes); + if (ret!=KErrNone) + { + passedToClient = EFalse; + } + } + else + { + /* + Message is not complete, but re-assembly store contains other constituent PDU + of the message which makes the message complete. + + In case of class 0 messages, it is possible to forward the incomplete messages. So after forwarding + the incomplete message, if we receive other constituent PDU of that message then in that case we + might receive all the constituent PDU of that message but aSmsMesssage will contain partial complete message. + */ + TReassemblyEntry entry; + CReassemblyStoreUtility::PopulateEntry(entry, aSmsMessage, 1); + + CSmsBuffer* buffer = CSmsBuffer::NewL(); + CSmsMessage* smsMessage = CSmsMessage::NewL(iFs, CSmsPDU::ESmsDeliver, buffer ); + CleanupStack::PushL( smsMessage ); + + CArrayFix* indexArray = new(ELeave) CArrayFixFlat(KFlatArrayGranularity); + CleanupStack::PushL(indexArray); + CArrayFixFlat* smsArray = new(ELeave) CArrayFixFlat(KFlatArrayGranularity); + CleanupStack::PushL(smsArray); + + GetSmsEntriesL(entry, *smsMessage, *indexArray, *smsArray); + SortPDUsL(*indexArray, *smsArray); + + CArrayFixFlat* tmpSmsArray = new(ELeave) CArrayFixFlat(KFlatArrayGranularity); + CleanupStack::PushL(tmpSmsArray); + + //Form the sequence of incomplete message & forward it to client. + TInt startSeqIndex(0), endSeqIndex(0); + TInt i; + do + { + i = startSeqIndex; + for (; i < indexArray->Count() - 1; i++) + { + if (indexArray->At(i) + 1 != indexArray->At(i+1)) + { + endSeqIndex = i; + break; + } + } + + if (i == (indexArray->Count() - 1)) + { + endSeqIndex = i; + } + + tmpSmsArray->Reset(); + for (TInt j = startSeqIndex; j <= endSeqIndex; j++) + { + tmpSmsArray->AppendL(smsArray->At(j)); + } + + if (tmpSmsArray->Count() == entry.Total()) + { + smsMessage->DecodeMessagePDUsL(*tmpSmsArray); + } + else + { + //Build the partial complete message. + if (endSeqIndex == (indexArray->Count() - 1)) + { + smsMessage->DecodePartialCompleteMessagePDUsL(*tmpSmsArray, ETrue); + } + else + { + smsMessage->DecodePartialCompleteMessagePDUsL(*tmpSmsArray, EFalse); + } + } + + //Forward the partial complete message to client. + TInt ret=KErrNone; + TRAP(ret, aSmsComm.ProcessMessageL(*smsMessage, aOriginalSmsAddr, aOriginalSmsMessage, aDes)); + if (ret != KErrNone) + { + passedToClient = EFalse; + } + startSeqIndex = endSeqIndex + 1; + endSeqIndex = startSeqIndex; + } + while (startSeqIndex < indexArray->Count()); + + CleanupStack::PopAndDestroy(4); //tmpSmsArray, indexArray, smsArray, smsMessage + } + + if(passedToClient) + { + SetMessagePassedToClientL(aSmsMessage); + } + + //If the latest segement exceeds the limitation, then we can't wait till ack. + //So call ProcessMessageIfExceedLimitationL() function & delete the oldest message. + if (IsExceedLimitation()) + { + ProcessMessageIfExceedLimitationL(aSmsComm); + } + } + +/** +It frees the space by forwarding the class 0 message if class 0 re-assembly store +exceeds limitation (max class 0 message, max reserved pdu segment). + +@param aSmsComm a reference to aSmsComm object which implemented the events. + +@internalComponent +*/ +void CClass0SmsReassemblyStore::ProcessMessageIfExceedLimitationL(MSmsComm& aSmsComm) + { + //Add Comment + TInt class0MsgCount = iEntryArray.Count(); + TInt noOfMsgStoredInPreAllocatedFile = iPreallocatedFile->NumberOfPDUStored(); + if ((class0MsgCount > iMaxClass0Msg) + || (noOfMsgStoredInPreAllocatedFile >= iMaxPDUSeg)) + { + CSmsBuffer* buffer = CSmsBuffer::NewL(); + CSmsMessage* smsMessage = CSmsMessage::NewL(iFs, CSmsPDU::ESmsDeliver, buffer ); + CleanupStack::PushL( smsMessage ); + + CArrayFix* indexArray = new(ELeave) CArrayFixFlat(KFlatArrayGranularity); + CleanupStack::PushL(indexArray); + CArrayFixFlat* smsArray = new(ELeave) CArrayFixFlat(KFlatArrayGranularity); + CleanupStack::PushL(smsArray); + + //Add Comment + TInt index; + if (class0MsgCount > iMaxClass0Msg) + { + index = GetIndexOfOldestMessage(); + } + else + { + index = GetIndexOfOldestMessageFromReservedFileL(); + } + + TReassemblyEntry entry = iEntryArray[index]; + + GetSmsEntriesL(entry, *smsMessage, *indexArray, *smsArray); + + SortPDUsL(*indexArray, *smsArray); + + CArrayFixFlat* tmpSmsArray = new(ELeave) CArrayFixFlat(KFlatArrayGranularity); + CleanupStack::PushL(tmpSmsArray); + + //Form the message & forward it to client. + TInt startSeqIndex(0), endSeqIndex(0); + TInt i; + do + { + i = startSeqIndex; + for (; i < indexArray->Count() - 1; i++) + { + if (indexArray->At(i) + 1 != indexArray->At(i+1)) + { + endSeqIndex = i; + break; + } + } + if (i == (indexArray->Count() - 1)) + { + endSeqIndex = i; + } + tmpSmsArray->Reset(); + + for (TInt j = startSeqIndex; j <= endSeqIndex; j++) + { + tmpSmsArray->AppendL(smsArray->At(j)); + } + + if (entry.IsComplete()) + { + if (tmpSmsArray->Count() == entry.Total()) + { + smsMessage->DecodeMessagePDUsL(*tmpSmsArray); + } + else + { + //Build the partial complete message. + if (endSeqIndex == (indexArray->Count() - 1)) + { + smsMessage->DecodePartialCompleteMessagePDUsL(*tmpSmsArray, ETrue); + } + else + { + smsMessage->DecodePartialCompleteMessagePDUsL(*tmpSmsArray, EFalse); + } + } + } + else + { + //Build the partial complete message. + smsMessage->DecodePartialCompleteMessagePDUsL(*tmpSmsArray, EFalse); + } + //Forward the partial complete message to client. + TBuf16 buffer; + TRAP_IGNORE(aSmsComm.ProcessMessageL(*smsMessage, NULL, NULL, buffer)); + + /* + Can't do much. So free the reserved space & also stored the information + related to forwarded message. If the message is complete then delete + the complete message (see the condition after the while loop)rather + than freeing the memory. No need to store the complete messages forwaded + info. + */ + if ((noOfMsgStoredInPreAllocatedFile > iMaxPDUSeg) && (!entry.IsComplete())) + { + SetIncompleteMessageForwardedToClientL(*smsMessage); + } + startSeqIndex = endSeqIndex + 1; + endSeqIndex = startSeqIndex; + } + while (startSeqIndex < indexArray->Count()); + //Can't do much. So removed the oldest message. + if ((class0MsgCount > iMaxClass0Msg) || + ((noOfMsgStoredInPreAllocatedFile > iMaxPDUSeg) && (entry.IsComplete()))) + { + DeleteMessageL(*smsMessage, EFalse); + } + CleanupStack::PopAndDestroy(4); //tmpSmsArray, indexArray, smsArray, smsMessage + } + } + +/** +Guard timer calls this function when timer expires. +It goes through all the messages stored in re-assembly store & forwards those message +whose time has expired. +It is also called when a new observer is added or a PDU has been received and successfully +processed. It is also called from ProcessCompleteClass0SmsMessagesL. + +@internalComponent +*/ +void CClass0SmsReassemblyStore::ProcessTimeoutMessageL() + { + LOGSMSPROT1("CClass0SmsReassemblyStore::ProcessTimeoutMessageL()"); + TBool passedToClient=ETrue; + TInt count=iEntryArray.Count(); + + CSmsBuffer* buffer = CSmsBuffer::NewL(); + CSmsMessage* smsMessage = CSmsMessage::NewL(iFs, CSmsPDU::ESmsDeliver, buffer ); + CleanupStack::PushL( smsMessage ); + + CArrayFix* indexArray = new(ELeave) CArrayFixFlat(KFlatArrayGranularity); + CleanupStack::PushL(indexArray); + CArrayFixFlat* smsArray = new(ELeave) CArrayFixFlat(KFlatArrayGranularity); + CleanupStack::PushL(smsArray); + + CArrayFixFlat* tmpSmsArray = new(ELeave) CArrayFixFlat(KFlatArrayGranularity); + CleanupStack::PushL(tmpSmsArray); + + TTime time; + time.UniversalTime(); + + TTimeIntervalSeconds timeOutInSecs = iGuardTimeOut*3600; + + for (TInt k=count-1; k>=0; k--) + { + if (!iEntryArray[k].PassedToClient()) + { + TTimeIntervalSeconds diffInSecs; + time.SecondsFrom(iEntryArray[k].Time(), diffInSecs); + indexArray->Reset(); + smsArray->Reset(); + if (diffInSecs >= timeOutInSecs) + { + GetSmsEntriesL(iEntryArray[k], *smsMessage, *indexArray, *smsArray); + SortPDUsL(*indexArray, *smsArray); + + //Form the message & forward it to client. + TInt startSeqIndex(0), endSeqIndex(0); + TInt i; + do + { + i = startSeqIndex; + for (; i < indexArray->Count() - 1; i++) + { + if (indexArray->At(i) + 1 != indexArray->At(i+1)) + { + endSeqIndex = i; + break; + } + } + if (i == (indexArray->Count() - 1)) + { + endSeqIndex = i; + } + tmpSmsArray->Reset(); + for (TInt j = startSeqIndex; j <= endSeqIndex; j++) + { + tmpSmsArray->AppendL(smsArray->At(j)); + } + + if (iEntryArray[k].IsComplete()) + { + if (tmpSmsArray->Count() == iEntryArray[k].Total()) + { + smsMessage->DecodeMessagePDUsL(*tmpSmsArray); + } + else + { + //Build the partial complete message. + if (endSeqIndex == (indexArray->Count() - 1)) + { + smsMessage->DecodePartialCompleteMessagePDUsL(*tmpSmsArray, ETrue); + } + else + { + smsMessage->DecodePartialCompleteMessagePDUsL(*tmpSmsArray, EFalse); + } + } + } + else + { + //Build the partial complete message. + if (endSeqIndex == (indexArray->Count() - 1)) + { + smsMessage->DecodePartialCompleteMessagePDUsL(*tmpSmsArray, ETrue); + } + else + { + smsMessage->DecodePartialCompleteMessagePDUsL(*tmpSmsArray, EFalse); + } + } + + //Forward the partial complete message to client. + TInt ret=KErrNone; + TBuf16 buffer; + ret = iSmsComm.ProcessMessageL(*smsMessage, NULL, NULL, buffer); + if (ret != KErrNone) + { + passedToClient = EFalse; + } + + startSeqIndex = endSeqIndex + 1; + endSeqIndex = startSeqIndex; + } + while (startSeqIndex < indexArray->Count()); + + if(passedToClient) + { + SetMessagePassedToClientL(*smsMessage); + } + } //diffInSecs >= timeOutInSecs + } //!iEntryArray[k].PassedToClient() + } //For k loop + CleanupStack::PopAndDestroy(4); //tmpSmsArray, indexArray, smsArray, smsMessage + } + +/** +It sets the bit-map of forwarded incomplete message to client. +It frees up the memory by removing the forwarded PDUs. + +@param aSmsMessage a reference to forwaded incomplete class 0 sms message object. + +@internalComponent +*/ +void CClass0SmsReassemblyStore::SetIncompleteMessageForwardedToClientL(const CSmsMessage& aSmsMessage) + { + LOGSMSPROT1("CClass0SmsReassemblyStore::SetIncompleteMessageForwardedToClientL()"); + TInt index = KErrNotFound; + + if (aSmsMessage.IsComplete()) + { + LOGSMSPROT1("This function must be called when message is incomplete"); + User::Leave(KErrArgument); + } + + if (FindMessageL(aSmsMessage, EFalse, index)) + { + CIncompleteClass0MessageInfo& incompleteClass0MsgInfo = (CIncompleteClass0MessageInfo &) aSmsMessage.GetOperationsForNonIEL(ESmsIncompleteClass0MessageParameter); + // Retrieve incomplete class 0 message information & process + TInt startPos, endPos; + TBool isLastMessage; + incompleteClass0MsgInfo.GetIncompleteMessageInfoL(startPos, endPos, isLastMessage); + + const TReassemblyEntry& entry = iEntryArray[index]; + + //Remove the forwarded PDU entries from pre-allocated file + TInt preAllocatedFileIndex=KErrNotFound; + iPreallocatedFile->MatchEntryToExistingMessage(entry, preAllocatedFileIndex); + if (preAllocatedFileIndex!=KErrNotFound) + { + BeginTransactionLC(); + iPreallocatedFile->RemovePDUsL(preAllocatedFileIndex, startPos, endPos); + iPreallocatedFile->StoreForwardedPDUsInfoL(preAllocatedFileIndex, startPos, endPos); + CommitTransactionL(); + } + else + { + //This situation should never arise + User::Leave(KErrNotFound); + } + + //Remove the forwarded PDU entries from permanent store file + TInt permanentStoreIndex=KErrNotFound; + iPermanentFileStore->MatchEntryToExistingMessage(entry, permanentStoreIndex); + if (permanentStoreIndex!=KErrNotFound) + { + TRAP_IGNORE(BeginTransactionLC(); + iPermanentFileStore->RemovePDUsL(permanentStoreIndex, startPos, endPos); + CommitTransactionL();); + } + } + } + +/** +This function is needed to ensure that permanent file +clean itself with pre-allocated file. +There migth be a scenario where user has deleted a message +but the corresponding message has not been deleted from permanent store file +due to out-of-disk condition. But the entry is invalid because +it is no more in the pre-allocated file which contains master header info. + +@return the error code. + +@internalComponent +*/ +TInt CClass0SmsReassemblyStore::CleanReassemblyEntries() + { + LOGSMSPROT1("CleanReassemblyEntries"); + const CArrayFix& preAllocatedFileEntryArray = iPreallocatedFile->Entries(); + TInt ret=KErrNone; + TRAP(ret, BeginTransactionLC(); + iPermanentFileStore->CleanupEntriesL(preAllocatedFileEntryArray); + CommitTransactionL();); + + if (ret == KErrDiskFull) + { + LOGSMSPROT1("CleanupEntriesL returns KErrDiskFull"); + /* + Get access to reserve memory, call again to clean the entries with compact. + Compact needs to be called at this instance because permanent store + will use reserve disk space to delete, but after deletion reserve + disk space will be freed. So compact will make sure that reserve disk + space is not used after compaction. + */ + iFs.GetReserveAccess(KStoreDrive); + TRAP(ret, BeginTransactionLC(); + iPermanentFileStore->CleanupEntriesWithCompactL(preAllocatedFileEntryArray); + CommitTransactionL();); + LOGSMSPROT2("CleanupEntriesWithCompactL returns %d", ret); + iFs.ReleaseReserveAccess(KStoreDrive); + } + return ret; + } + +/** +It returns the SMS entries (sms messages, sms pdu arrays & the corresponding index array) +from its permanent store & pre-allocated file. + +@param aEntry reference to entry information. The message which match this entry information + is returned to caller of the function. +@param aSmsMessage a reference to class 0 sms message object. +@param aIndexArray a reference to the array of pdu index. +@param aSmsArray a reference to the array of pdu. + +@internalComponent +*/ +void CClass0SmsReassemblyStore::GetSmsEntriesL(const TReassemblyEntry& aEntry, CSmsMessage& aSmsMessage, CArrayFix& aIndexArray, CArrayFix& aSmsArray) + { + LOGSMSPROT1("CClass0SmsReassemblyStore::GetSmsEntriesL()"); + TInt permanentStoreIndex=KErrNotFound; + iPermanentFileStore->MatchEntryToExistingMessage(aEntry, permanentStoreIndex); + if (permanentStoreIndex!=KErrNotFound) + { + iPermanentFileStore->InternalizeEntryL(permanentStoreIndex, aSmsMessage, aIndexArray, aSmsArray); + } + else + { + LOGSMSPROT1("No PDUs in Permanent store file"); + } + + TInt preAllocatedFileIndex=KErrNotFound; + iPreallocatedFile->MatchEntryToExistingMessage(aEntry, preAllocatedFileIndex); + if (preAllocatedFileIndex!=KErrNotFound) + { + iPreallocatedFile->InternalizeEntryL(preAllocatedFileIndex, aSmsMessage, aIndexArray, aSmsArray); + + //Filter forwarde PDU here..this condition might arise if the PDU + //could not be deleted at the time of OOD condition from permanent store file. + TSmsPreAllocatedFileStoreReassemblyEntry preAllocatedFileEntry = iPreallocatedFile->Entries()[preAllocatedFileIndex]; + if (preAllocatedFileEntry.NumberOfPDUsForwardToClient() > 0) + { + for (TInt j=aIndexArray.Count(); j>0 ;j--) + { + TUint8 bitMapIndex = (aIndexArray.At(j-1)-1)/8; + TUint8 bitPos = (aIndexArray.At(j-1)-1)%8; + TUint8 bitMap; + preAllocatedFileEntry.GetBitMap(bitMapIndex, bitMap); + TUint8 tmpBitMap = 1; + tmpBitMap <<= bitPos; + if (tmpBitMap == (bitMap & tmpBitMap)) + { + aIndexArray.Delete(j-1); + aSmsArray.Delete(j-1); + } + } + } + + if (permanentStoreIndex==KErrNotFound) + { + //In this scenario a CSmsMessage object has to be created from the existing PDU in + //pre-allocated file & then serialized into aSmsMessage. + LOGSMSPROT2("Number of PDUs in Pre-allocated file %d", aIndexArray.Count()); + if (aIndexArray.Count() > 0) + { + CSmsBuffer* smsBuffer = CSmsBuffer::NewL(); + CSmsMessage* smsMessage = CSmsMessage::NewL(iFs, aSmsArray.At(0), smsBuffer, EFalse, ETrue); + CleanupStack::PushL(smsMessage); + + //Update sms messages's properties. + smsMessage->SetStorage(aSmsMessage.Storage()); + smsMessage->SetStatus((NMobileSmsStore::TMobileSmsStoreStatus)aSmsMessage.Status()); + smsMessage->SetLogServerId(aSmsMessage.LogServerId()); + smsMessage->SetTime(aSmsMessage.Time()); + smsMessage->SetUTCOffset(aSmsMessage.UTCOffset()); + smsMessage->SetDecodedOnSIM(aSmsMessage.DecodedOnSim()); + smsMessage->SetForwardToClient(aSmsMessage.ForwardToClient()); + smsMessage->SetToFromAddressL(aSmsMessage.ToFromAddress()); + + CBufSeg* recvbufseg = CBufSeg::NewL(KSmsMaxSegmentLength); + CleanupStack::PushL(recvbufseg); + // Attempt to serialize this message into the buffer + RBufWriteStream writestream(*recvbufseg); + writestream.Open(*recvbufseg); + CleanupClosePushL(writestream); + writestream << *smsMessage; + + // Initialize the read stream with the buffer + RBufReadStream readstream(*recvbufseg); + readstream.Open(*recvbufseg,0); + CleanupClosePushL(readstream); + // De-serialize the message from using the read stream + readstream >> aSmsMessage; + CleanupStack::PopAndDestroy(4); //readstream, writestream, recvbufseg, smsMessage + } + } + } + } + +/* +It returns when the next timeout should occur. +*/ +void CClass0SmsReassemblyStore::GetNextGuardTimeout(TTime& aNextTimeout) + { + TTime curTime; + curTime.UniversalTime(); + TTimeIntervalSeconds timeOutInSecs = iGuardTimeOut*3600; + aNextTimeout = curTime + timeOutInSecs; + + TInt class0ReassemblyStoreCount = iEntryArray.Count(); + + if (class0ReassemblyStoreCount > 0) + { + for (TInt i=0; i curTime) && + (guradTimeout < aNextTimeout)) + { + aNextTimeout = guradTimeout; + } + } + } + } + +/* +It returns the index of oldest message in class 0 re-assembly store. +*/ +TInt CClass0SmsReassemblyStore::GetIndexOfOldestMessage() + { + TInt index = 0; + TTime time = iEntryArray[index].Time(); + for (TInt index2=1; index2 < iEntryArray.Count(); index2++) + { + if (iEntryArray[index2].Time() < time) + { + index = index2; + } + } + return index; + } + +/* +It returns the index of oldest message in class 0 re-assembly store. +*/ +TInt CClass0SmsReassemblyStore::GetIndexOfOldestMessageFromReservedFileL() + { + TInt index = KErrNotFound; + //Find the index of oldest message in pre-allocated file. + index = iPreallocatedFile->GetOldestMessageEntryIndex(); + if (index == KErrNotFound) + { + //This condition should never arise + User::Leave(KErrNotFound); + } + + //Compare the oldest pre-allocated message entry against class 0 reassembly store entry + //and return the index of class 0 reassembly store entry. + const TSmsPreAllocatedFileStoreReassemblyEntry& preAllocatedFileEntry = iPreallocatedFile->Entries()[index]; + + TInt reassemblyCount = iEntryArray.Count(); + TInt count; + for (count = 0; count < reassemblyCount; count++) + { + TReassemblyEntry& entry = iEntryArray[count]; + + if (entry.Reference() == preAllocatedFileEntry.Reference() && + entry.Total() == preAllocatedFileEntry.Total() && + entry.PduType() == preAllocatedFileEntry.PduType() && + entry.Storage() == preAllocatedFileEntry.Storage() && + entry.Description2() == preAllocatedFileEntry.Description2()) + { + // + // Found it! + // + index = count; + break; + } + } + if (count == reassemblyCount) + { + //This condition should never arise + User::Leave(KErrNotFound); + } + return index; + } + +/* +It returns a boolean value indicating whether the limitation (max class 0 message, +max pdu stored in pre-allocated file) imposed on class 0 reassembly store exceeds or not. +*/ +TBool CClass0SmsReassemblyStore::IsExceedLimitation() + { + TInt class0MsgCount = iEntryArray.Count(); + TInt noOfMsgStoredInPreAllocatedFile = iPreallocatedFile->NumberOfPDUStored(); + if ((class0MsgCount > iMaxClass0Msg) + || (noOfMsgStoredInPreAllocatedFile > iMaxPDUSeg)) + { + return ETrue; + } + + return EFalse; + } + +/* +This sorting algorithm is based on bubble sort +*/ +void CClass0SmsReassemblyStore::SortPDUsL(CArrayFix& aIndexArray, CArrayFix& aSmsArray) + { + //Test---Index array count must be equal to sms array count + TBool swapped; + + do + { + swapped = EFalse; + for (TInt i=0; i aIndexArray[i+1]) + { + aSmsArray.InsertL(i, aSmsArray[i+1]); + aSmsArray.Delete(i+2); + aIndexArray.InsertL(i, aIndexArray[i+1]); + aIndexArray.Delete(i+2); + swapped = ETrue; + } + } + } + while (swapped); + } + +/** + * Utility func for cleanup stack + */ +void CClass0StoreCloseObject(TAny* aObj) + { + LOGGSMU2("WARNING! Hey, CClass0StoreCloseObject called by Untrapper! [0x%08x]", aObj); + ((CClass0SmsReassemblyStore*)aObj)->Revert(); + } + +/** + * Sets the class 0 re-assembly store as in-transaction. + * + * The function checks the validity of the call and leaves KErrAccessDenied if + * invalid. + * @capability None + */ +void CClass0SmsReassemblyStore::BeginTransactionLC() + { + LOGSMSPROT3("CClass0SmsReassemblyStore::BeginTransactionLC [this=0x%08X iInTransaction=%d]", this, iInTransaction); + + if (iInTransaction) + { + LOGGSMU1("WARNING CClass0SmsReassemblyStore::BeginTransactionLC leaving with KErrAccessDenied"); + User::Leave(KErrAccessDenied); + } + + TCleanupItem class0StoreClose(CClass0StoreCloseObject, this); + CleanupStack::PushL(class0StoreClose); + iPreallocatedFile->BeginTransactionL(); + iPermanentFileStore->BeginTransactionL(); + iInTransaction = ETrue; + } + +/** + * It commits the transaction. + */ +void CClass0SmsReassemblyStore::CommitTransactionL() + { + LOGSMSPROT3("CClass0SmsReassemblyStore::CommitTransactionL(): this=0x%08X iInTransaction=%d", + this, iInTransaction); + + //Commit permanent store file + iPermanentFileStore->CommitTransactionL(); + //Commit pre-allocated file + iPreallocatedFile->CommitTransactionL(); + CleanupStack::Pop(this); //CClass0StoreCloseObject + iInTransaction = EFalse; + } + +/** + * It reverts the transaction. + */ +void CClass0SmsReassemblyStore::Revert() + { + LOGSMSPROT3("CClass0SmsReassemblyStore::Revert(): this=0x%08X, iInTransaction=%d", + this, iInTransaction); + + iPreallocatedFile->Revert(); + iPermanentFileStore->Revert(); + iInTransaction = EFalse; + }