// 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 <s32strm.h>
#include <ssm/ssmstatemanager.h>
#ifdef SYMBIAN_SSM_FLEXIBLE_MERGE
#include <e32cmn.h>
#endif
#include <ssm/ssmcommand.h>
#include <ssm/ssmcommandfactory.h>
#include "ssmcommandlistimpl.h"
#include "ssmdebug.h"
#include "ssmpanic.h"
#include "ssmcommandbase.h"
#include "ssmcommandparameters.h"
#include "ssmdeferreddeleter.h"
#define __IN_RANGE( x, y ) ( (x >= 0) && (x < y) )
//
// Construct a CSsmCommandListImplImpl object
//
CSsmCommandListImpl* CSsmCommandListImpl::NewL()
{
CSsmCommandListImpl* self = new(ELeave) CSsmCommandListImpl;
CleanupStack::PushL(self);
self->ConstructL();
CleanupStack::Pop(self);
return self;
}
/**
Used to create an instance of CSsmCommandListImpl class from CSsmCommandListImpl object
Must be used only by CLE
@param aSsmCommandListImpl CSsmCommandListImpl reference
@param aUtilProvider CSsmCommandUtilProvider pointer
@return A pointer to an object of type CSsmCommandListImpl.
*/
CSsmCommandListImpl* CSsmCommandListImpl::NewL(const CSsmCommandListImpl* aSsmCommandListImpl, CSsmCommandUtilProvider* aUtilProvider)
{
CSsmCommandListImpl* self = new(ELeave) CSsmCommandListImpl;
CleanupStack::PushL(self);
self->ConstructL(aSsmCommandListImpl, aUtilProvider);
CleanupStack::Pop(self);
return self;
}
CSsmCommandListImpl::CSsmCommandListImpl()
{
}
void CSsmCommandListImpl::ConstructL()
{
//iDeferredDeleter is initialized inside the ConstuctL... when CLE constructs the commandlist
//where as for unit testing we need to construct the iDeferredDeleter here for testing purposes
#ifdef SSM_CMD_TESTFRAMEWORK
iDeferredDeleter = CSsmDeferredDeleter::NewL();
#endif
iResourceFileName.CreateL(0);
}
/**
Used to reconstruct the commandlist from the commandlist recieved from SSM server.
It should be used only by CLE.
@param CSsmCommandListImpl aSsmCommandListImpl pointer
@param CSsmCommandUtilProvider aUtilProvider pointer
*/
void CSsmCommandListImpl::ConstructL(const CSsmCommandListImpl* aSsmCommandListImpl, CSsmCommandUtilProvider* aUtilProvider)
{
SetDelayBetweenCommands(aSsmCommandListImpl->DelayBetweenCommands());
// It is ok for this pointer to be passed between threads as the policy
// will remain loaded until CLE returns
SetConditionalCallback(aSsmCommandListImpl->ConditionalCallback());
iDeferredDeleter = CSsmDeferredDeleter::NewL();
#ifndef SYMBIAN_SSM_FLEXIBLE_MERGE
// Write out the resource filename
iResourceFileName.ReAllocL(aSsmCommandListImpl->iResourceFileName.Size());
#endif
const TInt count = aSsmCommandListImpl->iCmdList.Count();
TArray<MSsmCommand*> deferredList = PendingDeferred();
for (TInt i = 0; i < count; i++)
{
CSsmCommandBase* cmd = SsmCommandFactory::ConstructCommandFromCommandLC(*aSsmCommandListImpl->iCmdList[i], deferredList,aUtilProvider);
const TSsmCommandType cmdType = cmd->Type();
if (ESsmCmdMultipleWait == cmdType)
{
iDeferredList.Reset();
}
else if (ESsmDeferredWaitForSignal == cmd->ExecutionBehaviour())
{
iDeferredList.AppendL(cmd);
}
iCmdList.AppendL(cmd);
CleanupStack::Pop(cmd);
}
}
//
// Destructor
//
CSsmCommandListImpl::~CSsmCommandListImpl()
{
const TInt count = iCmdList.Count();
for (TInt i = 0; i < count; i++)
{
if (iCmdList[i])
iCmdList[i]->Release();
}
iCmdList.Close();
iDeferredList.Close();
delete iDeferredDeleter;
delete iResourceFile;
iResourceFileName.Close();
}
//
// Read a set of commands into the list from a stream
//
void CSsmCommandListImpl::InternalizeL( RReadStream& aReadStream , CSsmCommandUtilProvider* aUtilProvider )
{
if( (0 != iCmdList.Count()) || (0 != iDeferredList.Count()) )
{
SSMLOGLEAVE( KErrAlreadyExists );
}
iDelayBetweenCommands = aReadStream.ReadInt32L();
// It is ok for this pointer to be passed between threads as the policy
// will remain loaded until CLE returns
iConditionalCallback = reinterpret_cast<MSsmConditionalCallback*>(aReadStream.ReadInt32L());
// Read in the resource filename
iResourceFileName.ReAllocL(aReadStream.ReadInt32L());
aReadStream.ReadL(iResourceFileName);
TUint32 count = aReadStream.ReadUint32L();
while (count--)
{
TInt conditionalInformation = aReadStream.ReadInt32L();
const TSsmCommandType type = static_cast<TSsmCommandType>(aReadStream.ReadUint16L());
TArray<MSsmCommand*> deferredList = PendingDeferred();
CSsmCommandBase* cmd = SsmCommandFactory::ConstructCommandFromStreamLC(type, aReadStream, deferredList);
// The ssmswppolicyserver and ssmswppolicycli code internalizes and externalizes
// the commandlist in order to pass the it from server to the client.
// This commandlist is not executed by ssmswppolicycli, it just passes it to the cle where the commandlist is executed
// and hence it does not require the utilprovider to be passed in to this method.
if (aUtilProvider)
{
cmd->SetUtilProvider(*aUtilProvider);
}
else
{
DEBUGPRINT1(_L("CSsmCommandListImpl::InternalizeL used by ssmswppolicycli code so Utilprovider is not required"));
}
cmd->SetConditionalInformation(conditionalInformation);
#ifdef SYMBIAN_SSM_FLEXIBLE_MERGE
if( cmd->ConditionalInformation() != 0 )
{
HBufC* tempFileName = HBufC::NewLC(aReadStream, KMaxFileName);
cmd->SetCommandResourceFileNameL(*tempFileName);
CleanupStack::PopAndDestroy(tempFileName);
}
#endif
AppendL(cmd);
CleanupStack::Pop(cmd);
}
}
//
// Write a set of commands from the list to a stream
//
void CSsmCommandListImpl::ExternalizeL( RWriteStream& aWriteStream ) const
{
aWriteStream.WriteInt32L(iDelayBetweenCommands);
// It is ok for this pointer to be passed between threads as the policy
// will remain loaded until CLE returns
aWriteStream.WriteInt32L(reinterpret_cast<TInt>(iConditionalCallback));
// Write out the resource filename
aWriteStream.WriteInt32L(iResourceFileName.Length());
aWriteStream.WriteL(iResourceFileName);
const TInt count = iCmdList.Count();
aWriteStream.WriteUint32L(count);
for (TInt i = 0; i < count; i++)
{
const MSsmCommand* const cmd = iCmdList[i];
// Write out the conditional information
aWriteStream.WriteInt32L(cmd->ConditionalInformation());
// Write out the type
const TSsmCommandType type = cmd->Type();
aWriteStream.WriteInt16L(type);
// Write out the command specific information
cmd->ExternalizeL(aWriteStream);
#ifdef SYMBIAN_SSM_FLEXIBLE_MERGE
if (cmd->ConditionalInformation())
{
const CSsmCommandBase* cmdBase = static_cast<const CSsmCommandBase*>(cmd);
if (cmdBase)
{
aWriteStream << cmdBase->GetCommandResourceFileName();
}
}
#endif
}
}
//
// Execute a specific command in the list
//@panic ECmdListBadIdx if the index value is out of range
//@panic ECmdNullPtr if the information used to create command is null
void CSsmCommandListImpl::Execute( const TInt aIndex, TRequestStatus& aStatus )
{
__ASSERT_DEBUG( __IN_RANGE(aIndex, iCmdList.Count()), PanicNow(KPanicCmdList, ECmdListBadIdx));
__ASSERT_DEBUG( iCmdList[ aIndex ], PanicNow(KPanicCmdList, ECmdNullPtr));
MSsmCommand* cmd = iCmdList[ aIndex ];
if( cmd )
{
// Load the resource file if there is conditional information
if( cmd->ConditionalInformation() != 0 )
{
#ifdef SYMBIAN_SSM_FLEXIBLE_MERGE
CSsmCommandBase* cmdBase = static_cast<CSsmCommandBase*>(cmd);
if (cmdBase)
{
if (iResourceFileName != cmdBase->GetCommandResourceFileName())
{
TInt length = cmdBase->GetCommandResourceFileNameLength();
if (iResourceFileName.MaxLength() < length)
{
TRAPD(err, iResourceFileName.ReAllocL(length));
//iResourceFileName is set to KNullDesC if there is any error(like KErrNoMemory)
//InitialiseResourceFileL() leaves in this case and can be handled by evaluate condition
iResourceFileName = (err == KErrNone) ? cmdBase->GetCommandResourceFileName() : KNullDesC;
}
else
{
iResourceFileName = cmdBase->GetCommandResourceFileName();
}
if (iResourceFile)
{
delete iResourceFile;
iResourceFile = NULL;
}
}
}
#endif
// TRAP_IGNORE is used here as if this method fails then
// iResourceFile will be null, but evaluate condition can handle
// iResourceFile being null.
TRAP_IGNORE(InitialiseResourceFileL());
}
if(iCmdList[ aIndex ]->EvaluateCondition(iConditionalCallback, iResourceFile))
{
// Condition is true so execute the command
iCmdList[ aIndex ]->Execute( aStatus );
}
else
{
// Condition is false so complete aStatus immediately
// The EvaluateCondition() method will have set the command
// correctly to allow completion of deferred wait for signal commands
TRequestStatus* trs = &aStatus;
aStatus = KRequestPending;
User::RequestComplete(trs, KErrNone);
}
}
}
//
// Cancel a specific command in the list
//@panic ECmdListBadIdx if the index value is out of range
//@panic ECmdNullPtr if the information used to create command is null
void CSsmCommandListImpl::ExecuteCancel( const TInt aIndex )
{
__ASSERT_DEBUG( __IN_RANGE(aIndex, iCmdList.Count()), PanicNow(KPanicCmdList, ECmdListBadIdx));
__ASSERT_DEBUG( iCmdList[ aIndex ], PanicNow(KPanicCmdList, ECmdNullPtr));
if( iCmdList[aIndex] )
{
iCmdList[ aIndex ]->ExecuteCancel();
}
}
//
// Count of items in the list
//
TInt CSsmCommandListImpl::Count()const
{
return iCmdList.Count();
}
const MSsmCommand* CSsmCommandListImpl::operator[](TInt aIndex) const
{
return iCmdList[aIndex];
}
void CSsmCommandListImpl::AppendL(MSsmCommand* aCmd)
{
if (!aCmd)
{
SSMLOGLEAVE(KErrArgument);
}
const TSsmCommandType cmdType = aCmd->Type();
DEBUGPRINT2A("Adding command type: %d", cmdType);
if (ESsmCmdPublishSystemState == cmdType)
{
++iPublishSysStateCmdCount;
}
else if (ESsmCmdPublishSwp == cmdType)
{
++iPublishSwpCmdCount;
}
#ifdef SYMBIAN_SSM_FLEXIBLE_MERGE
TLinearOrder<MSsmCommand> order(CSsmCommandListImpl::ComparePriority);
iCmdList.InsertInOrderAllowRepeatsL(aCmd,order);
#else
iCmdList.AppendL(aCmd);
#endif // SYMBIAN_SSM_FLEXIBLE_MERGE
}
void CSsmCommandListImpl::GetDataToValidateCommandlist(TInt& aPublishSystemStateCount, TInt& aPublishSwpCount) const
{
aPublishSystemStateCount = iPublishSysStateCmdCount;
aPublishSwpCount = iPublishSwpCmdCount;
}
#ifdef SYMBIAN_SSM_FLEXIBLE_MERGE
TInt CSsmCommandListImpl::ComparePriority(const MSsmCommand& aFirst, const MSsmCommand& aSecond)
{
const CSsmCommandBase* const firstCommand = static_cast<const CSsmCommandBase*>(&aFirst);
const CSsmCommandBase* const secondCommand = static_cast<const CSsmCommandBase*>(&aSecond);
return (secondCommand->Priority() - firstCommand->Priority());
}
#endif
TArray<MSsmCommand*> CSsmCommandListImpl::PendingDeferred() const
{
return iDeferredList.Array();
}
void CSsmCommandListImpl::SetDelayBetweenCommands(TInt aDelayBetweenCommands)
{
iDelayBetweenCommands = aDelayBetweenCommands;
}
TInt CSsmCommandListImpl::DelayBetweenCommands() const
{
return iDelayBetweenCommands;
}
/**
@panic ECmdNullPtr if the information used to create command is null
*/
void CSsmCommandListImpl::DeferredDelete()
{
__ASSERT_DEBUG(iDeferredDeleter, PanicNow(KPanicCmdList, ECmdNullPtr));
TInt indexOfPendingCommand = PendingCommandIndex();
if ((KErrNotFound == indexOfPendingCommand) || !iDeferredDeleter)
{
delete this;
}
else
{
iDeferredDeleter->DeferredDelete(this);
iDeferredDeleter = NULL;
}
}
TInt CSsmCommandListImpl::PendingCommandIndex()
{
TInt index = KErrNotFound;
const TInt count = iCmdList.Count();
for (TInt i = 0; i < count; i++)
{
MSsmCommand* cmd = iCmdList[i];
// command list can not be deleted if any deferred command is pending
if (cmd->IsDeferred() && (KRequestPending == cmd->CompletionStatus()))
{
index = i;
break;
}
}
return index;
}
void CSsmCommandListImpl::InitialiseResourceFileL()
{
if(!iResourceFile)
{
// Need to load and allocate the resource file
RFs rfs;
User::LeaveIfError(rfs.Connect());
CleanupClosePushL(rfs);
RFile file;
User::LeaveIfError(file.Open(rfs, iResourceFileName, EFileRead | EFileShareReadersOnly));
CleanupClosePushL(file);
// read entire resource file into a buffer
TInt fileSize;
User::LeaveIfError(file.Size(fileSize));
RBuf8 buf;
buf.CreateL(fileSize);
CleanupClosePushL(buf);
User::LeaveIfError(file.Read(buf));
// Create the resource file
iResourceFile = CResourceFile::NewL(buf);
// Clean up everything
CleanupStack::PopAndDestroy(&buf);
CleanupStack::PopAndDestroy(&file);
CleanupStack::PopAndDestroy(&rfs);
}
}
void CSsmCommandListImpl::SetResourceFileNameL(const TDesC& aFileName)
{
#ifdef SYMBIAN_SSM_FLEXIBLE_MERGE
// this is not needed in other case as there is only one resource file per substate
TInt length = iResourceFileName.MaxLength();
if (length < aFileName.Length())
{
iResourceFileName.ReAllocL(aFileName.Length());
}
#else
iResourceFileName.ReAllocL(aFileName.Length());
#endif
iResourceFileName.Copy(aFileName);
// Clear iResourceFile as it is no longer in sync with iResourceFileName
if(iResourceFile)
{
delete iResourceFile;
iResourceFile = NULL;
}
}
void CSsmCommandListImpl::SetConditionalCallback(MSsmConditionalCallback& aCallBack)
{
iConditionalCallback = &aCallBack;
}
MSsmConditionalCallback& CSsmCommandListImpl::ConditionalCallback() const
{
return *iConditionalCallback;
}
/**
Returns command's severity.
*/
TCmdErrorSeverity CSsmCommandListImpl::Severity(TInt aCommandIndex)const
{
return iCmdList[aCommandIndex]->Severity(); ;
}