diff -r 000000000000 -r 4e1aa6a622a0 sysstatemgmt/systemstatemgr/ssm/src/ssmstatetransitionengine.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sysstatemgmt/systemstatemgr/ssm/src/ssmstatetransitionengine.cpp Tue Feb 02 00:53:00 2010 +0200 @@ -0,0 +1,539 @@ +// Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies). +// All rights reserved. +// This component and the accompanying materials are made available +// under the terms of "Eclipse Public License v1.0" +// which accompanies this distribution, and is available +// at the URL "http://www.eclipse.org/legal/epl-v10.html". +// +// Initial Contributors: +// Nokia Corporation - initial contribution. +// +// Contributors: +// +// Description: +// + +#include + +#include "ssmdebug.h" +#include "ssmserverpanic.h" +#include "ssmstatetransitionengine.h" +#include "ssmstatepolicyresolverproxy.h" +#include "clesessionproxy.h" +#include "ssmstatetransitionrequest.h" +#include "ssmstatepolicyframe.h" +#include "ssmcommandlistutils.h" + + +CSsmStateTransitionEngine* CSsmStateTransitionEngine::NewL(MSsmStatePolicyResolverProxy* aResolver, MCleSessionProxy* aCleSession) + { + CSsmStateTransitionEngine* self = CSsmStateTransitionEngine::NewLC(aResolver, aCleSession); + CleanupStack::Pop(self); + return self; + } + +CSsmStateTransitionEngine* CSsmStateTransitionEngine::NewLC(MSsmStatePolicyResolverProxy* aResolver, MCleSessionProxy* aCleSession) + { + __ASSERT_DEBUG(aResolver, PanicNow(KPanicSysStateMgr,ESsmStateEngineError5)); + __ASSERT_DEBUG(aCleSession, PanicNow(KPanicSysStateMgr,ESsmStateEngineError2)); + CSsmStateTransitionEngine* self = new (ELeave) CSsmStateTransitionEngine(aResolver, aCleSession); + CleanupStack::PushL(self); + return self; + } + +CSsmStateTransitionEngine::CSsmStateTransitionEngine(MSsmStatePolicyResolverProxy* aResolver, MCleSessionProxy* aCleSession) + : CActive(CActive::EPriorityStandard), + iResolver(*aResolver), + iCleSession(*aCleSession), + iPerformCommandListValidation(ETrue), + iNextAction(EUnConnected) + { + CActiveScheduler::Add(this); + } + +CSsmStateTransitionEngine::~CSsmStateTransitionEngine() + { + CActive::Cancel(); + + iCleSession.ReleaseCle(); + iResolver.ReleasePolicyResolver(); + + delete iQueuedTransition; + delete iCurrentTransition; + delete iCommandList; + } + +TSsmStateTransition const* CSsmStateTransitionEngine::CurrentTransition() const + { + return iCurrentTransition ? &(iCurrentTransition->StateTransition()) : NULL; + } + +TSsmStateTransition const* CSsmStateTransitionEngine::QueuedTransition() const + { + return iQueuedTransition ? &(iQueuedTransition->StateTransition()) : NULL; + } + +MSsmStatePolicy::TResponse CSsmStateTransitionEngine::TransitionAllowed(const TSsmStateTransition& aRequest, const RMessagePtr2& aMessage) + { + return Policy()->CallTransitionAllowed(aRequest, CurrentTransition(), QueuedTransition(), aMessage); + } + +void CSsmStateTransitionEngine::SubmitL(const TSsmStateTransition& aRequest) + { + CSsmStateTransitionRequest* request = new (ELeave) CSsmStateTransitionRequest(aRequest); + DoSubmit(request); + } + +void CSsmStateTransitionEngine::SubmitL(const TSsmStateTransition& aRequest, const RMessage2& aMessage) + { + CSsmStateTransitionRequest* request = new (ELeave) CSsmStateTransitionRequest(aRequest, aMessage); + DoSubmit(request); + } +/** +If the current slot is free, aRequest is assigned to the current-slot (the queue-slot should be free). +If the current slot is occupied, aRequest is assigned to the queue-slot. Any already existing +queued request is lost. + +(You should adhere to the TResponse given by the policy's TransitionAllowed before calling this method) +*/ +void CSsmStateTransitionEngine::DoSubmit(CSsmStateTransitionRequest* aRequest) + { + if(iCurrentTransition) + { + //ECurrentRemainReplaceQueued + if(iQueuedTransition) + { + iQueuedTransition->Complete(KErrCancel); + delete iQueuedTransition; + iQueuedTransition = NULL; + } + iQueuedTransition = aRequest; + } + else + { + //EReplaceCurrentClearQueue + __ASSERT_DEBUG(NULL == iQueuedTransition, PanicNow(KPanicSysStateMgr,ESsmStateEngineError10)); // should be already be deleted by DoCancel + if(iQueuedTransition) + { + iQueuedTransition->Complete(KErrCancel); + delete iQueuedTransition; + iQueuedTransition = NULL; + } + iCurrentTransition = aRequest; + } + + if(!IsActive()) + { + if (EUnConnected == iNextAction) + { + Start(); + } + else if (EIdle == iNextAction) + { + iNextAction = iCleSession.IsConnected() ? EStartTransition : EUnConnected; + Start(); + } + } + } + +void CSsmStateTransitionEngine::Start() + { + SetActive(); + TRequestStatus* status = &iStatus; + User::RequestComplete(status, KErrNone); + } + +void CSsmStateTransitionEngine::Cancel(CSession2* aSession) + { + if(aSession) + { + //Client side request to cancel + if(iQueuedTransition && iQueuedTransition->OriginatesFrom(aSession)) + { + iQueuedTransition->Complete(KErrCancel); + delete iQueuedTransition; + iQueuedTransition = NULL; + } + + if(iCurrentTransition) + { + if(iCurrentTransition->OriginatesFrom(aSession) && !InTransition()) + { + CActive::Cancel(); + ShiftQueueIfNeeded(); + if(iCurrentTransition) + { + iNextAction = EStartTransition; + Start(); + } + } + } + } + else + { + //Server-side request, raised when the policy allows a new request with EReplaceCurrentClearQueue + if(iQueuedTransition) + { + iQueuedTransition->Complete(KErrCancel); + delete iQueuedTransition; + iQueuedTransition = NULL; + } + + if(iCurrentTransition) + { + CActive::Cancel(); + ShiftQueueIfNeeded(); + } + } + } + +void CSsmStateTransitionEngine::DoCancel() + { + //Cancel any pending request this engine has submitted to external providers + CSsmStatePolicyFrame* policy = iResolver.Policy(); + if(policy &&(iNextAction == EPrepareCommandList)) + { + policy->CallInitializeCancel(); + } + else if(policy &&(iNextAction == EExecuteCommandList)) + { + policy->CallPrepareCommandListCancel(); + } + else if(iNextAction == EReadNextTransition) + { + iCleSession.ExecuteCommandListCancel(); + //iCommandList needs to be deleted when cancel has been called before completion of command + if (iCommandList) + { + delete iCommandList; + iCommandList = NULL; + } + } + + //Then Cancel the pending operations this engine has been asked to do + if(iCurrentTransition) + { + iCurrentTransition->Complete(KErrCancel); + delete iCurrentTransition; + iCurrentTransition = NULL; + } + + //Reset internal state + if(iNextAction > EUnConnected) + { + iNextAction = EIdle; + } + } + +void CSsmStateTransitionEngine::RunL() + { + if(iNextAction != EReadNextTransition) + { + //Propagate all problems to RunError except the return value from CleSrv + SSMLOGLEAVEIFERROR(iStatus.Int()); //will leave in release builds as well + } + + switch(iNextAction) + { + case EUnConnected: + DoConnectCleSessionL(); + iNextAction = EStartTransition; + break; + + case EStartTransition: + DoStartTransitionL(); + iNextAction = EInitialize; + break; + + case EInitialize: + DoInitialize(); + iNextAction = EPrepareCommandList; + break; + + case EPrepareCommandList: + DoPrepareCommandList(); + iNextAction = EExecuteCommandList; + break; + + case EExecuteCommandList: + DoExecuteCommandList(); + iNextAction = EReadNextTransition; + break; + + case EReadNextTransition: + { + if (iCommandList) + { + delete iCommandList; + iCommandList = NULL; + } + TBool furtherTransition = FurtherTransition(); + iNextAction = iCleSession.IsConnected() ? EStartTransition : EUnConnected; + if(!furtherTransition && !iCurrentTransition) + { + iNextAction = EIdle; // If no further transition and nothing was in queue --> set to idle + } + } + break; + + case EIdle: + default: + __ASSERT_DEBUG(EFalse, PanicNow(KPanicSysStateMgr,ESsmStateEngineError11)); + break; + } + } + +/** + Most errors cause a Panic followed by Kernel fault. The rationale behind this is that all + the state policies placed on the rom by the device manufacturer must be error free when + the device is shipped. There is no case for graceful handling of missing commandlists, + bad resource files or an unknown state. + + The only error which does not cause a panic is the return value from CleSrv. When a command + execution results in an error, CleSrv reports the error-code back to CSsmStateTransitionEngine + which forwards the error-code to the State Policy DLL in the call MSsmStatePolicy::GetNextState + */ +TInt CSsmStateTransitionEngine::RunError(TInt aError) + { + DEBUGPRINT3(_L("CSsmStateTransitionEngine RunError: %d occured in operation: %d"), aError, iNextAction); + + TSsmStateName name = iTargetSystemState.Name(); + switch(iNextAction) + { + case EUnConnected: + { + iCurrentTransition->Complete(aError); + DEBUGPRINT3(_L("State transition to %S failed, could not connect to CleSrv, reason: %d"), &name, aError); + PanicNow(KPanicSysStateMgr,ESsmStateEngineError3); + break; + } + case EStartTransition: + { + DEBUGPRINT3(_L("State transition to %S failed, possibly due to policy file err: %d"), &name, aError); + PanicNow(KPanicSysStateMgr,ESsmStateEngineError7); + break; + } + case EInitialize: + case EPrepareCommandList: + { + DEBUGPRINT3(_L("State Policy DLL %S failed to initialize, leavecode: %d"), &name, aError); + PanicNow(KPanicSysStateMgr,ESsmStateEngineError6); + break; + } + case EExecuteCommandList: + { + DEBUGPRINT3(_L("Command list execution failed, target state %S, error: %d"), &name, aError); + PanicNow(KPanicSysStateMgr,ESsmStateEngineError8); + break; + } + case EReadNextTransition: + { + //This error will not be raised from the usual User::LeaveIfError at the top of RunL, but from a bad + //virtual implementation of GetNextState (which leaves despite this should be a non-leaving function). + DEBUGPRINT3(_L("State Policy error: %d occured during a call to GetNextState %S"), aError, &name); + PanicNow(KPanicSysStateMgr,ESsmStateEngineError4); + break; + } + default: + case EIdle: + __ASSERT_DEBUG(EFalse, PanicNow(KPanicSysStateMgr,ESsmStateEngineError15)); + break; + } + + return KErrNone; + } //lint !e529 not subsequently referenced - dependent on debug + +/** + Before this transition engine is used for the first time, we need to connect + to (and probably start) the CleSrv. + The session remains connected because we do not want to risk not being able to + connect to CleSrv (due to low memory in kernel) if we get a request to transition + to state ESsmFail. + */ +void CSsmStateTransitionEngine::DoConnectCleSessionL() + { + iCleSession.ConnectL(); + Start(); + } + +void CSsmStateTransitionEngine::DoStartTransitionL() + { + //Let the client know this transition has started, from now on this transition can't be cancelled + // note: in case this is called as part of a FurtherTransition(), complete will do nothing + iCurrentTransition->Complete(KErrNone); + +#ifdef _DEBUG + TSsmStateName name = iCurrentTransition->State().Name(); + DEBUGPRINT2(_L("Initiating transition to state %S."), &name); +#endif + + //Start off by loading the correct state policy DLL + iTargetSystemState = iCurrentTransition->State(); + iResolver.GetStatePolicyL(iTargetSystemState); + Start(); + } + +void CSsmStateTransitionEngine::DoInitialize() + { + if(!Policy()->Initialized()) + { + //Let the state policy do whatever it needs to do for example initialize its CSsmCommandListResourceReader + Policy()->CallInitialize(iStatus); + SetActive(); + } + else + { + Start(); + } + } + +void CSsmStateTransitionEngine::DoPrepareCommandList() + { + __ASSERT_DEBUG(iCurrentTransition, PanicNow(KPanicSysStateMgr,ESsmStateEngineError13)); + Policy()->CallPrepareCommandList(iTargetSystemState, iCurrentTransition->Reason(), iStatus); + SetActive(); + } + +void CSsmStateTransitionEngine::DoExecuteCommandList() + { + iCommandList = Policy()->CallCommandList(); + if(iPerformCommandListValidation) + { + DoValidation(); + } + + if(iCommandList) + { + iCleSession.ExecuteCommandList(*iCommandList, iStatus, iSeverity); + SetActive(); + } + else + { + Start(); + } + } + +void CSsmStateTransitionEngine::DoValidation() + { + TBool valid = EFalse; + if(!iCommandList) + { + DEBUGPRINT1(_L("State Policy DLL returned NULL from CommandList()")); + } + else + { + valid = CSsmCommandListUtils::IsValidStateList(*iCommandList); + if(!valid) + { + DEBUGPRINT1(_L("State Policy DLL returned an invalid CommandList()")); + } + } + __ASSERT_ALWAYS( iCommandList && valid, PanicNow(KPanicSysStateMgr,ESsmStateEngineError9)); + } + +TBool CSsmStateTransitionEngine::FurtherTransition() + { + __ASSERT_DEBUG(iCurrentTransition, PanicNow(KPanicSysStateMgr,ESsmStateEngineError14)); + const TInt error = iStatus.Int(); + + // In case the server is no longer present + if(error == KErrServerTerminated) + { + // Closes the cle handle and marks it as disconnected + iCleSession.Close(); + } + // Severity must be obtained from CLE somehow. This needs proper implementation. + TCmdErrorSeverity severity = ECmdIgnoreFailure; + if(KErrNone != error) + { + DEBUGPRINT3(_L("Commandlist execution reported error: %d, with severity %d."), error, severity); + severity = ECmdCriticalSeverity; + } + + TSsmState nextSystemState; + const TBool moreTransitions = Policy()->CallGetNextState(iTargetSystemState, + iCurrentTransition->Reason(), + error, + severity, + nextSystemState); + if(!moreTransitions) + { + // Request completed +#ifdef _DEBUG + TSsmStateName name = iCurrentTransition->State().Name(); + DEBUGPRINT2(_L("Transition to state %S completed."), &name); +#endif + delete iCurrentTransition; + iCurrentTransition = NULL; + // See if there is another request queued + ShiftQueueIfNeeded(); + } + else + { + // FurtherTransition requested +#ifdef _DEBUG + TSsmStateName name = nextSystemState.Name(); + DEBUGPRINT2(_L("FurtherTransition requested to state %S."), &name); +#endif + // Update the target system state (Note: We only update the SystemState, same reason is being re-used) + const TSsmStateTransition request(nextSystemState, iCurrentTransition->Reason()); + iCurrentTransition->SetStateTransition(request); + } + + if(moreTransitions || iCurrentTransition) + { + Start(); + } + + return moreTransitions; + } + +TBool CSsmStateTransitionEngine::InTransition() const + { + return (iNextAction > EStartTransition); + } + +void CSsmStateTransitionEngine::ShiftQueueIfNeeded() + { + if(iQueuedTransition && !iCurrentTransition) + { + iCurrentTransition = iQueuedTransition; + iQueuedTransition = NULL; + } + } + +CSsmStatePolicyFrame* CSsmStateTransitionEngine::Policy() + { + CSsmStatePolicyFrame* policy = iResolver.Policy(); + __ASSERT_DEBUG( policy, PanicNow(KPanicSysStateMgr,ESsmStateEngineError1)); + return policy; + } + +void CSsmStateTransitionEngine::PerformCommandListValidation(TBool aSetting) + { + iPerformCommandListValidation = aSetting; +#ifdef _DEBUG + if(iPerformCommandListValidation) + { + DEBUGPRINT1(_L("Turning on State command list validation")); + } + else + { + DEBUGPRINT1(_L("Turning off State command list validation")); + } +#endif + } + +/** + * Only for test purposes + * Cleanup the transition engine + */ + #ifdef _DEBUG +void CSsmStateTransitionEngine::CleanupTransitionEngine() + { + iResolver.ReleasePolicyResolver(); + delete iQueuedTransition; + delete iCurrentTransition; + iCleSession.ReleaseCle(); + } +#endif