--- /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 <ssm/ssmcommandlist.h>
+
+#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