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