genericservices/taskscheduler/SCHSVR/SchTimer.cpp
changeset 0 e4d67989cc36
child 64 c44f36bb61a3
child 67 a1e347446159
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/genericservices/taskscheduler/SCHSVR/SchTimer.cpp	Tue Feb 02 02:01:42 2010 +0200
@@ -0,0 +1,630 @@
+// Copyright (c) 2004-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:
+//
+
+// User includes
+#include "SchTimer.h"
+#include "SCHMAN.H"
+#include "SCHEDULE.H"
+
+// system includes
+#include <e32property.h>
+
+/**
+An instance of this class is used for each different time that a
+schedule needs to be run. It is created solely in the 
+CScheduleCriteriaManager class.  The timer is updated via the 
+SetNext method.  When the time has been reached it notifies the schedule
+manager via the CScheduleCriteriaManager::DueSchedule() method.
+
+@internalComponent
+*/
+NONSHARABLE_CLASS(CScheduleTimer) : public CTimer
+	{
+public:
+	~CScheduleTimer();
+	static CScheduleTimer* NewL(TInt aSchedule, CScheduleCriteriaManager& aManager);
+	
+	void SetNext(const TTsTime& aNextTime);
+	TInt Id();
+
+	//list capability
+	static TInt Offset();
+private:
+	// From CTimer
+	void RunL();
+
+	CScheduleTimer(TInt aSchedule, CScheduleCriteriaManager& aManager);
+	void ConstructL();
+	
+private:
+	TTsTime iDueTime;
+	TInt iScheduleHandle;
+	TSglQueLink iLink;
+	CScheduleCriteriaManager& iConditonManager;
+	};
+
+CScheduleTimer* CScheduleTimer::NewL(TInt aSchedule, CScheduleCriteriaManager& aManager)
+	{
+	CScheduleTimer* self = new(ELeave) CScheduleTimer(aSchedule, aManager);
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+CScheduleTimer::CScheduleTimer(TInt aSchedule, CScheduleCriteriaManager& aManager)
+:	CTimer(EPriorityStandard),
+	iScheduleHandle(aSchedule),
+	iConditonManager(aManager)
+	{
+	}
+
+CScheduleTimer::~CScheduleTimer()
+	{
+	Cancel();
+	}
+
+void CScheduleTimer::ConstructL()
+	{
+	CTimer::ConstructL();
+	CActiveScheduler::Add(this);
+	}
+
+void CScheduleTimer::SetNext(const TTsTime& aNewTime)
+	{
+	// Can't handle (unlikely but theoretical) situation when year is BC (not AD)
+	__ASSERT_ALWAYS(aNewTime.GetUtcTime().DateTime().Year()>0,User::Invariant());
+	if (IsActive())
+		Cancel();
+
+	iDueTime=aNewTime;
+	TTime currentTime;
+	currentTime.UniversalTime();
+
+	if	(aNewTime.GetUtcTime()>currentTime)
+		AtUTC(aNewTime.GetUtcTime());
+	else
+		AtUTC(currentTime);
+	}
+
+//	Respond to an task being due.  RunL is only called once!
+void CScheduleTimer::RunL()
+	{
+	if(iStatus != KErrAbort)
+		iConditonManager.DueSchedule(iScheduleHandle);
+
+	// RunL() will also be triggered if the system time is changed, with iStatus
+	// set to KErrAbort. In this case DueSchedule() should not be called.
+	// If the system time has been changed, the schedule needs to be requeued. 
+	// This has already been done automatically by CTaskScheduler::HandleEnvironmentChange()
+	// [called by the AO CEnvironmentChangeNotifier],
+	// as the active object CEnvironmentChangeNotifier has a higher priority than CScheduleTimer.
+	}	
+
+TInt CScheduleTimer::Offset()
+	{
+	return (_FOFF(CScheduleTimer, iLink));
+	}
+
+TInt CScheduleTimer::Id()
+	{
+	return iScheduleHandle;
+	}
+
+//		
+// class CPropertyNotifier
+// This class handles changes to P&S variables and notifies the
+// CConditionManager class when a condition is satisfied.
+NONSHARABLE_CLASS(CPropertyNotifier) : public CActive
+	{
+public:
+	~CPropertyNotifier();
+	static CPropertyNotifier* NewL(CConditionManager& aManager);
+	
+private:	
+	CPropertyNotifier(CConditionManager& aManager);
+	void AttachL();
+	// From CActive
+	void RunL();
+	void DoCancel();
+
+public:
+	void SetPropertyL(const TUid& aCategory, TUint aKey);
+	
+private:
+	TUid iCategory;
+	TUint iKey;
+	RProperty iProperty;
+	CConditionManager& iConditionManager;
+	};
+
+//		
+// class CConditionManager
+// This class manages a set of conditions for each schedule.  It is used
+// solely by the CScheduleCriteriaManager class. When the set of conditions 
+// is met, a the schedule manager is notified.
+NONSHARABLE_CLASS(CConditionManager) : public CActive
+	{
+public:
+	~CConditionManager();
+	static CConditionManager* NewL(TInt aSchedule, CScheduleCriteriaManager& aManager);
+	
+private:	
+	CConditionManager(TInt aSchedule, CScheduleCriteriaManager& aManager);
+	TBool MatchAllConditionsL() const;
+	TBool HasVariable(const TUid& aCategory, TUint aKey) const;
+	void CompleteRequest();
+	// From CActive
+	void RunL();
+	void DoCancel();
+
+public:
+	void ReplaceL(const RArray<TTaskSchedulerCondition>& aConditions);
+	TInt Id();
+	void VariableChangedL(const TUid& aCategory, TUint aKey);
+	//list capability
+	static TInt Offset();	
+	
+private:
+	RArray<TTaskSchedulerCondition> iConditions;
+	RPointerArray<CPropertyNotifier> iPropertyNotifiers;
+	TInt iScheduleHandle;
+	TSglQueLink iLink;
+	CScheduleCriteriaManager& iManager;
+	};
+
+CConditionManager* CConditionManager::NewL(TInt aSchedule, CScheduleCriteriaManager& aManager)
+	{
+	CConditionManager* self = new(ELeave) CConditionManager(aSchedule, aManager);
+	return self;
+	}
+
+CConditionManager::CConditionManager(TInt aSchedule, CScheduleCriteriaManager& aManager)
+:	CActive(EPriorityStandard+1), //make priority higher that propertynotifier AO
+	iScheduleHandle(aSchedule),
+	iManager(aManager)
+	{
+	CActiveScheduler::Add(this);
+	}
+
+CConditionManager::~CConditionManager()
+	{
+	Cancel();
+	iPropertyNotifiers.ResetAndDestroy();
+	iConditions.Reset();		
+	}
+
+//This function evaluates (aValue <op> aState) expression, where "op" could be
+//"==", "!=", ">", "<", depending on aType value, and returns the result of expression.
+static TBool DoMatchCondition(TTaskSchedulerCondition::TConditionType aType,
+							  TInt aValue, 
+							  TInt aState)
+	{
+	if(aType == TTaskSchedulerCondition::EEquals)
+		{
+		if(aValue == aState)
+			{
+			return ETrue;
+			}
+		}
+	else if(aType == TTaskSchedulerCondition::ENotEquals)
+		{
+		if(aValue != aState)
+			{
+			return ETrue;
+			}
+		}
+	else if(aType == TTaskSchedulerCondition::EGreaterThan)
+		{
+		if(aValue > aState)
+			{
+			return ETrue;
+			}
+		}
+	else if(aType == TTaskSchedulerCondition::ELessThan)
+		{
+		if(aValue < aState)
+			{
+			return ETrue;
+			}
+		}
+	else
+		{
+		__ASSERT_ALWAYS(0, User::Invariant());
+		}
+	return EFalse;
+	}
+
+void CConditionManager::ReplaceL(const RArray<TTaskSchedulerCondition>& aConditions)
+	{
+	// Ensure any active requests are cancelled
+	if(IsActive()) 
+	{ 
+		Cancel(); 
+	}
+
+	//destroying existing ones will cancel outstanding requests
+	iPropertyNotifiers.ResetAndDestroy(); 
+	iConditions.Reset();
+	const TInt count = aConditions.Count();
+	TInt i;
+	//Check that the properties already exist
+	for(i=0;i<count;++i)
+		{
+		TInt value;
+		TInt err = RProperty::Get(aConditions[i].iCategory, aConditions[i].iKey, value);
+		if(err != KErrNone)
+			{
+			if(err == KErrNotFound)
+				{
+				err = KErrArgument; //use KErrArgument error code to signify bad conditions.
+				}
+			User::Leave(err);	
+			}
+		}
+	//Add the new conditions and notifiers.
+	for(i=0;i<count;++i)
+		{
+		//Create local CPropertyNotifier object
+		CPropertyNotifier* notifier = CPropertyNotifier::NewL(*this);
+		CleanupStack::PushL(notifier);
+		const TTaskSchedulerCondition& condition = aConditions[i];
+		notifier->SetPropertyL(condition.iCategory, condition.iKey); 
+		//Add condition
+		User::LeaveIfError(iConditions.Append(condition));
+		//Add notifier
+		TInt err = iPropertyNotifiers.Append(notifier);
+		if(err != KErrNone)
+			{
+			iConditions.Remove(iConditions.Count() - 1);//Remove the condition we've just added
+			User::Leave(err);
+			}
+		CleanupStack::Pop(notifier);
+		}
+	//Check to see that conditions are not already satisfied.
+	if(MatchAllConditionsL())
+		{
+		SetActive(); //we need to set AO active here, otherwise RunL wont be called.	
+		CompleteRequest();
+		}
+	}
+
+void CConditionManager::CompleteRequest()
+	{
+	TRequestStatus *status = &iStatus;
+	User::RequestComplete(status, KErrNone); // now compete request so RunL is triggered
+	}
+	
+//Respond to a condition changing.
+//Called from CPropertyNotifier::RunL().
+void CConditionManager::VariableChangedL(const TUid& aCategory, TUint aKey)
+	{
+	//We have been notified that the value of one of the variables has been changed.
+	//It is not enough to check that the variable's value satisfies its condition!
+	//We have to check that all CConditionManager::iPropertyNotifiers satisfy their conditions.
+	//----------------
+	//If this is a variable, which is a part of the variables, monitored by the 
+	//current CConditionManager object, only then do check variables values against
+	//requested conditions	
+	if(HasVariable(aCategory, aKey))
+		{
+		if(MatchAllConditionsL())
+			{
+			SetActive(); //we need to set AO active here, otherwise RunL wont be called.	
+			CompleteRequest();
+			}
+		}
+	}
+	
+void CConditionManager::RunL()
+	{
+	// cancel outstanding notification requests by destroying AO's
+	iPropertyNotifiers.ResetAndDestroy();
+	iManager.DueSchedule(iScheduleHandle);		
+	}
+
+void CConditionManager::DoCancel()
+	{
+	CompleteRequest();  		
+	}
+
+//The method returns ETrue, if all monitored variables (aConditions array) 
+//satisfy their conditions, EFalse otherwise.
+TBool CConditionManager::MatchAllConditionsL() const
+	{
+	TInt satisfiedConditionsCnt = 0;
+	TInt count = iConditions.Count();
+	for(TInt i=0;i<count;++i)
+		{
+		const TTaskSchedulerCondition& condition = iConditions[i];
+		TInt value;
+		// errors here typically indicate that the P&S variables is not of 
+		// integer type (ie its changed) or its been deleted
+		User::LeaveIfError(RProperty::Get(condition.iCategory, condition.iKey, value));
+		if(::DoMatchCondition(condition.iType, value, condition.iState))
+			{
+			++satisfiedConditionsCnt;
+			}
+		}
+	return satisfiedConditionsCnt == count;
+	}
+	
+//This method checks if the variable, identified by (aCategory, aKey) pair, is a part
+//of CConditionManager::iPropertyNotifiers array and returns ETrue, if that's true.
+//EFalse otherwise.
+TBool CConditionManager::HasVariable(const TUid& aCategory, TUint aKey) const
+	{
+	for(TInt i=iConditions.Count()-1;i>-1;--i)
+		{
+		if(iConditions[i].iCategory == aCategory && iConditions[i].iKey == aKey)
+			{
+			return ETrue;
+			}
+		}
+	return EFalse;
+	}
+
+TInt CConditionManager::Offset()
+	{
+	return (_FOFF(CConditionManager, iLink));
+	}
+
+TInt CConditionManager::Id()
+	{
+	return iScheduleHandle;
+	}
+
+//
+
+CPropertyNotifier* CPropertyNotifier::NewL(CConditionManager& aManager)
+	{
+	CPropertyNotifier* self = new(ELeave) CPropertyNotifier(aManager);
+	return self;
+	}
+
+CPropertyNotifier::CPropertyNotifier(CConditionManager& aManager)
+:	CActive(EPriorityStandard),
+	iConditionManager(aManager)
+	{
+	CActiveScheduler::Add(this);
+	}
+
+CPropertyNotifier::~CPropertyNotifier()
+	{
+	Cancel();
+	}
+
+void CPropertyNotifier::AttachL()
+	{
+	User::LeaveIfError(iProperty.Attach(iCategory, iKey));
+	iProperty.Subscribe(iStatus);
+	SetActive();
+	}
+
+void CPropertyNotifier::SetPropertyL(const TUid& aCategory, TUint aKey)
+	{
+	if (IsActive())
+		Cancel();
+	iCategory = aCategory;
+	iKey = aKey;
+	AttachL();	
+	}
+
+//	Respond to a condition changing
+void CPropertyNotifier::RunL()
+	{
+	if (iStatus.Int() >= KErrNone)
+		{
+		iConditionManager.VariableChangedL(iCategory, iKey);
+		AttachL();
+		}
+	// if status is KErrNotFound then P&S variable has been deleted!  By 
+	// resubscribing we wait for it to be created.  If it never gets
+	// created then TRequestStatus never completes so this condition 
+	// never gets met and iConditionManager.VariableChanged which 
+	// makes sense.
+	else if (iStatus.Int() == KErrNotFound)
+		AttachL();			
+	// If status is another error we have a problem!!! Whatever the case 
+	// we should just ignore this condition from now on by doing nothing.
+	}
+
+void CPropertyNotifier::DoCancel()
+	{
+	iProperty.Cancel();
+	}
+	
+	
+	
+//
+
+CScheduleCriteriaManager* CScheduleCriteriaManager::NewL(CTaskScheduler& aOwner)
+	{
+	CScheduleCriteriaManager* self = new(ELeave) CScheduleCriteriaManager(aOwner);
+	return self;
+	}
+
+CScheduleCriteriaManager::~CScheduleCriteriaManager()
+	{
+	Cancel();
+	RemoveTimers();
+	RemoveConditions();
+	}
+
+CScheduleCriteriaManager::CScheduleCriteriaManager(CTaskScheduler& aOwner)
+:	CActive(EPriorityStandard+2), //make priority higher than condition AO
+	iTaskScheduler(aOwner),
+	iTimers(CScheduleTimer::Offset()),
+	iConditions(CConditionManager::Offset())	
+	{
+	CActiveScheduler::Add(this);
+	}
+
+void CScheduleCriteriaManager::CompleteRequest()
+	{
+	TRequestStatus *status = &iStatus;
+	User::RequestComplete(status, KErrNone); // now compete request so RunL is triggered	
+	}
+	
+void CScheduleCriteriaManager::DueSchedule(TInt aScheduleHandle)
+	{
+	iDueScheduleHandle = aScheduleHandle;
+	SetActive(); // need to set AO active so RunL will subsequently be called.
+	CompleteRequest();
+	}
+
+void CScheduleCriteriaManager::RunL()
+	{
+	// remove schedule and then notify task scheduler manager	
+	RemoveSchedule(iDueScheduleHandle);
+	iTaskScheduler.DueTaskNotifyL(iDueScheduleHandle);
+	}
+
+void CScheduleCriteriaManager::DoCancel()
+	{
+	CompleteRequest();
+	}
+	
+// If schedule timer for this ID doesnt exist then create and add new timer.  If schedule 
+// timer does exist then just amend existing timer.
+//When one of the schedule entries in this schedule has become due,
+//this function will be called with aNotFirstTime = ETrue
+//If this function is called because of environment changes then aSchChange   = EOnlyTime and only update time based schedule
+void CScheduleCriteriaManager::ReplaceScheduleL(CSchedule& aSchedule, TSchChangeType aSchChange , TBool aNotFirstTime)
+	{
+	aSchedule.CalculateDueTime(aNotFirstTime);
+
+	TInt  scheduleId 		=  	aSchedule.Id();
+	const TTsTime nextTime 	= 	aSchedule.DueTime();
+	ReplaceScheduleL(nextTime,scheduleId);
+	
+	//If this function is called because of environment changes then
+	//leave conditions unchanged
+	if(aSchChange == EOnlyTime)
+		return;
+	CConditionManager* condition = FindCondition(scheduleId);
+	// no point in doing work for 
+	if(aSchedule.Conditions().Count() > 0)
+		{
+		if(!condition)
+			{
+			condition = CConditionManager::NewL(scheduleId, *this);
+			iConditions.AddLast(*condition);
+			}
+		condition->ReplaceL(aSchedule.Conditions());
+		}
+	else if(condition)
+		RemoveCondition(condition);		
+	}
+	
+// If schedule timer for this ID doesnt exist then create and add new timer.  If schedule 
+// timer does exist then just amend existing timer.
+void CScheduleCriteriaManager::ReplaceScheduleL(const TTsTime& aNextTime, 
+								TInt aSchedule)
+	{
+	CScheduleTimer* timer = Find(aSchedule);
+	// if time is set to MaxTTime then we don't want to set a timer
+	// off as it will complete straight away.
+	if((aNextTime.GetUtcTime() != Time::MaxTTime())
+	&& 	(aNextTime.GetLocalTime() != Time::MaxTTime()))
+		{
+		if(!timer)
+			{	
+			timer = CScheduleTimer::NewL(aSchedule, *this);
+			iTimers.AddLast(*timer);
+			}
+		timer->SetNext(aNextTime);
+		}
+	else if(timer)	
+		{
+		RemoveTimer(timer); // make sure we remove the old one!
+		}
+	}
+
+void CScheduleCriteriaManager::RemoveSchedule(TInt aSchedule)
+	{
+	CScheduleTimer* timer = Find(aSchedule);
+	if(timer)
+		RemoveTimer(timer); // remove timer also terminates AO
+
+	CConditionManager* condition = FindCondition(aSchedule);
+	if(condition)
+		RemoveCondition(condition); // remove condition also terminates AO
+	
+	}
+
+//Timer methods	
+void CScheduleCriteriaManager::RemoveTimers()
+	{
+	CScheduleTimer* timer;
+	TSglQueIter<CScheduleTimer> timerIter(iTimers);
+    timerIter.SetToFirst();
+    while ((timer = timerIter++) != NULL)
+		{
+		RemoveTimer(timer);
+		}
+	}
+
+void CScheduleCriteriaManager::RemoveTimer(CScheduleTimer* aTimer)
+	{
+	iTimers.Remove(*aTimer);
+	delete aTimer;
+	}
+	
+CScheduleTimer* CScheduleCriteriaManager::Find(TInt aSchedule)
+	{
+	CScheduleTimer* timer = NULL;
+	TSglQueIter<CScheduleTimer> timerIter(iTimers);
+    timerIter.SetToFirst();
+    while ((timer = timerIter++) != NULL)
+		{
+		if	(timer->Id() == aSchedule)
+			break;
+		}	
+	return timer;
+	}	
+
+// condition methods
+void CScheduleCriteriaManager::RemoveConditions()
+	{
+	CConditionManager* condition;
+	TSglQueIter<CConditionManager> conditionIter(iConditions);
+    conditionIter.SetToFirst();
+    while ((condition = conditionIter++) != NULL)
+		{
+		RemoveCondition(condition);
+		}
+	}
+
+void CScheduleCriteriaManager::RemoveCondition(CConditionManager* aCondition)
+	{
+	iConditions.Remove(*aCondition);
+	delete aCondition;
+	}
+	
+CConditionManager* CScheduleCriteriaManager::FindCondition(TInt aSchedule)
+	{
+	CConditionManager* condition = NULL;
+	TSglQueIter<CConditionManager> conditionIter(iConditions);
+    conditionIter.SetToFirst();
+    while ((condition = conditionIter++) != NULL)
+		{
+		if	(condition->Id() == aSchedule)
+			break;
+		}	
+	return condition;
+	}	
+	
+