diff -r 9f5ae1728557 -r db3f5fa34ec7 messagingfw/scheduledsendmtm/schedulesendmtm/src/MsvScheduleSend.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/messagingfw/scheduledsendmtm/schedulesendmtm/src/MsvScheduleSend.cpp Wed Nov 03 22:41:46 2010 +0530 @@ -0,0 +1,1661 @@ +// Copyright (c) 1999-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: +// + +#ifdef _DEBUG +#undef _MSG_NO_LOGGING +#endif + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#ifndef _MSG_NO_LOGGING +#include +#endif + + +_LIT(KSchSendExe, "schsendexe.exe"); + +LOCAL_C TBool operator==(const CArrayFixFlat& aConditions1, const CArrayFixFlat& aConditions2) + { + TInt count1 = aConditions1.Count(); + TInt count2 = aConditions2.Count(); + + // Compare the conditions. + if( count1 != count2 ) + { + // Different number of conditions - no match. + return EFalse; + } + + // Search for all the conditions in info1 are in info2 + for( TInt ii = 0; ii < count1; ++ii ) + { + TTaskSchedulerCondition condition1 = aConditions1[ii]; + TTaskSchedulerCondition condition2; + TBool found = EFalse; + TInt jj = 0; + while( !found && jj < count2 ) + { + condition2 = aConditions2[jj]; + + if( condition2.iKey == condition1.iKey ) + found = ETrue; + else + ++jj; + } + if( !found || condition2.iState != condition1.iState || condition2.iType != condition1.iType ) + { + // Either aConditions2 has not got this condition or it has but it + // does not match - no match. + return EFalse; + } + } + return ETrue; + } + +/** +Constructor. + +@param aServerEntry +The CMsvServerEntry of the server MTM of which this CMsvScheduleSend +object is a member. +*/ +EXPORT_C CMsvScheduleSend::CMsvScheduleSend(CMsvServerEntry& aServerEntry) + : iServerEntry(aServerEntry) + { + } + +/** +Second phase constructor. + +This constructor creates instances of the following classes: +CMsvScheduleSettings, CMsvOffPeakTimes, CMsvSendErrorActions, +CMsvSysAgentActions and CMsvScheduledEntries. +*/ +EXPORT_C void CMsvScheduleSend::ConstructL() + { + iSettings = CMsvScheduleSettings::NewL(); + iOffPeakTimes = new (ELeave) CMsvOffPeakTimes(); + iErrorActions = CMsvSendErrorActions::NewL(); + iAgentActions = new (ELeave) CMsvSysAgentActions(); + iSchEntries = new (ELeave) CMsvScheduledEntries(KMsvSchsendArrayGrowth); + + iSchEntryInfo = new (ELeave) CArrayFixFlat(KMsvSchsendArrayGrowth); + iSchTaskInfo = new (ELeave) CArrayFixFlat(KMsvSchsendArrayGrowth); + } + +/** +Destructor. +*/ +EXPORT_C CMsvScheduleSend::~CMsvScheduleSend() + { + delete iSettings; + delete iOffPeakTimes; + delete iErrorActions; + delete iAgentActions; + + if (iScheduler.Handle()) + iScheduler.Close(); + + if (iSchEntries) + iSchEntries->ResetAndDestroy(); + + delete iSchEntries; + + delete iSchTaskInfo; + delete iSchEntryInfo; + } + +/** +Schedules messages on the task scheduler. + +Messages that are successfully scheduled have their sending state set to +KMsvSendStateScheduled. + +@param aSelection +Array of message IDs that need to be scheduled. This array cannot be empty. +All the messages identified in the selection must belong to the same MTM; +be scheduled for the same time; have the same setting for their OffPeak() +flag; have the scheduled time stored in the iDate member of their +corresponding TMsvEntry. + +@param aPackage +Scheduling options + +@leave Any error code but KErrLocked and KErrNotFound +The method overloading CMsvScheduledEntry::GetMessageL() left with an error, +i.e. the scheduling info of one of the messages from the selection could not +be retrieved from the message server. + +@leave Any error code +Unable to reset the previous scheduling info for a message. + +@panic ScheduleSend-DLL 0 +The array of message IDs is empty. +Debug build only. + +@panic ScheduleSend-DLL 1 +At least one of the selected messages is scheduled for a different time +as the others. +Debug build only. + +@panic ScheduleSend-DLL 2 +At least one of the selected messages does not belong to the same MTM. +Debug build only. + +@panic ScheduleSend-DLL 3 +At least one of the selected messages does not have the same off-peak +settings as the others. +Debug build only. +*/ +EXPORT_C void CMsvScheduleSend::ScheduleL(const CMsvEntrySelection& aSelection, const TMsvSchedulePackage& aPackage) + { + __ASSERT_DEBUG(aSelection.Count() > 0, gPanic(EMessageSelectionEmpty)); + + iPackage = aPackage; + GetMessagesL(aSelection); //Leaves with KErrNotFound if there + //are no messages returned in schEntries + TInt entryCount = iSchEntries->Count(); + SCHSENDLOG(FLog(_L8("Asked to schedule %d msgs"), entryCount)); + + if (entryCount) + { + TTime startTime; + +#if defined(_DEBUG) + CMsvScheduledEntry* firstEntry = (*iSchEntries)[0]; +#endif + + while (entryCount--) + { + CMsvScheduledEntry* message = iSchEntries->At(entryCount); + __ASSERT_DEBUG(firstEntry->ScheduleDate() == message->ScheduleDate(), gPanic(EMessagesNotSameTime)); + + startTime = message->ScheduleDate(); + + // Reset previous scheduling info + DeleteScheduleForEntryL(*message); + ResetScheduleInfoForEntryL(*message, EFalse); + } + + //Schedule the messages + DoScheduleL(*iSchEntries, KMsvSendStateScheduled, startTime, EFalse); + } + } + +/** +Determines when the messages should be re-scheduled on the task scheduler, +then schedules the messages at the new time(s). + +Messages that are successfully re-scheduled are updated. The pending conditions +flag indicates whether the message has been schedule for a set of conditions +being met (or a timeout occuring) or scheduled for a specified time/date. + +NOTE - conditions scheduling is only supoprted from 8.1 onwards. + +In the case of time-scheduling, the date field is the scheduled time/date. In +the case of conditions-scheduling, the date field reflects the timeout value. + +There are several cases when messages are not re-scheduled. If all recipients +have been sent to - in this case the message's sending state set to +KMsvSendStateSent. If, more commonly, the message's maximum number of re-tries +has been exceeded or the error action was ESendActionFail then the message is +not changed. + +@param aSelection +Array of message IDs that need to be re-scheduled. This array cannot be empty. +All the messages identified must belong to the same MTM. It is not a +precondition that each message has already been scheduled on the task +scheduler. + +@param aPackage +Scheduling options. + +@param aErrorAction +The specific action to take with the messages. If this argument is omitted, +then ReScheduleL() uses the iError member of each TMsvEntry to find the +related TMsvSendErrorAction in iErrorActions. + +@panic ScheduleSend-DLL 0 +The array of message IDs is empty. +Debug build only. +*/ +EXPORT_C void CMsvScheduleSend::ReScheduleL(const CMsvEntrySelection& aSelection, const TMsvSchedulePackage& aPackage, const TMsvSendErrorAction* aErrorAction) + { + __ASSERT_DEBUG(aSelection.Count() > 0, gPanic(EMessageSelectionEmpty)); + iPackage = aPackage; + GetMessagesL(aSelection); + + TInt entryCount = iSchEntries->Count(); + SCHSENDLOG(FLog(_L8("Asked to re-schedule %d msgs"), entryCount)); + + if (entryCount) + { + TTime curTime; + curTime.UniversalTime(); + curTime += iSettings->Latency(); + + while (entryCount--) + { + CMsvScheduledEntry* message = iSchEntries->At(entryCount); + + SCHSENDLOG(FLog(_L8("\tAttempting to Re-Schedule msg %d"), message->Id())); + + if (!SetMessageStartTime(*message, curTime, aErrorAction)) + { + SCHSENDLOG(FLog(_L8("\t\tCannot Re-Schedule msg %d (new sending state %d)"), message->Id(), message->SendingState())); + DeleteScheduleForEntryL(*message); + ResetScheduleInfoForEntryL(*message, ETrue); + delete message; + iSchEntries->Delete(entryCount); + } + } + + //Send the messages if there are any left to send + if (iSchEntries->Count()) + { + DoReScheduleL(*iSchEntries); + } + else + { + SCHSENDLOG(FLog(_L8("\tNo messages to Re-Schedule"))); + } + } + } + +/** +Delete the schedules for the specified messages from the task scheduler. + +The messages themselves are not deleted. + +@param aSelection +Array of message IDs that need to be deleted from the task scheduler. + +@leave Any error code +Unable to connect and register with the scheduler. + +@leave Any error code but KErrLocked and KErrNotFound +The method overloading CMsvScheduledEntry::GetMessageL() left with an error, +i.e. the scheduling info of one of the messages from the selection could not +be retrieved from the message server. + +@leave Any error code +Unable to reset the previous scheduling info for a message. +*/ +EXPORT_C void CMsvScheduleSend::DeleteScheduleL(const CMsvEntrySelection& aSelection) + { + ConnectAndRegisterL(); + + GetMessagesL(aSelection); + + TInt entryCount = iSchEntries->Count(); + SCHSENDLOG(FLog(_L8("Asked to delete schedule of %d msgs"), entryCount)); + + while (entryCount--) + { + CMsvScheduledEntry* message = iSchEntries->At(entryCount); + SCHSENDLOG(FLog(_L8("\tDelete schedule for msg %d (mtm %d, oldSendState %d, newSendState %d)"), message->Id(), message->Mtm().iUid, message->SendingState(), KMsvSendStateSuspended)); + message->SetSendingState(KMsvSendStateSuspended); + DeleteScheduleForEntryL(*message); + ResetScheduleInfoForEntryL(*message, ETrue); + } + } + +/* + Sets the message's scheduled flag to EFalse + Resets the schedule data associated with each message + Resets the number of retries for each recipient + Stores the data and recipients (in a stream associated with the TMsvEntry) +*/ +void CMsvScheduleSend::SendingCompleteL(CMsvScheduledEntry& aMessage, const TBool aChangeEntry) + { + __ASSERT_DEBUG(iServerEntry.Entry().Id() == aMessage.Id(), gPanic(EServerEntryNotSetToCorrectEntry)); + + aMessage.SetScheduled(EFalse); + aMessage.iData.Reset(); + aMessage.RecipientsResetRetries(); + + if (aChangeEntry) + { + TMsvEntry entry = iServerEntry.Entry(); + aMessage.Entry(entry); + User::LeaveIfError(iServerEntry.ChangeEntry(entry)); + } + + CMsvStore* store = iServerEntry.EditStoreL(); + CleanupStack::PushL(store); + aMessage.StoreL(*store); + store->CommitL(); + CleanupStack::PopAndDestroy(store); + } + +/** +Tells the scheduler that sending is complete. + +This method sets the messages's scheduled flag to false, resets the schedule +data associated with each message and the number of retries for each +recipient and stores the data and recipients in a stream associated with +the TMsvEntry. + +@param aSelection +Messages that were either successfully sent or which failed all the attempts +to send. +*/ +EXPORT_C void CMsvScheduleSend::SendingCompleteL(const CMsvEntrySelection& aSelection) + { + TInt count = aSelection.Count(); + + while (count--) + { + CMsvScheduledEntry* schEntry = GetMessageL(aSelection[count]); + CleanupStack::PushL(schEntry); + SendingCompleteL(*schEntry, ETrue); + CleanupStack::PopAndDestroy(); //schEntry + } + } + +/** +Tells the scheduler that sending is complete. + +This function must be called when a message that had previously been scheduled +is either sent or has failed. This function: + +1. Deletes the TMsvEntryScheduleData associated with the message + +2. Sets the Scheduled flag to EFalse + +3. If required, calls ChangeEntry() on the message server entry + +Note: SendingCompleteL() does not change the sending state of each message, +nor delete each message from the task scheduler. + +@param aEntry +The message which was either successfully sent or which failed (all the +attempts) to send. It is not a precondition that the message has already +been scheduled on the task scheduler. + +@param aChangeEntry +If aChangeEntry is ETrue then SendingCompleteL() will call +CMsvServerEntry::ChangeEntry() to update the message on the message server. + +@panic ScheduleSend-DLL 24 +The server entry is not set to the correct entry. +Debug build only. +*/ +EXPORT_C void CMsvScheduleSend::SendingCompleteL(TMsvEntry& aEntry, const TBool aChangeEntry) + { + __ASSERT_DEBUG(iServerEntry.Entry().Id() == aEntry.Id(), gPanic(EServerEntryNotSetToCorrectEntry)); + + CMsvScheduledEntry* schEntry = GetMessageL(aEntry.Id()); + CleanupStack::PushL(schEntry); + + SendingCompleteL(*schEntry, EFalse); + schEntry->Entry(aEntry); + + if (aChangeEntry) + { + User::LeaveIfError(iServerEntry.ChangeEntry(aEntry)); + } + + CleanupStack::PopAndDestroy(); //schEntry + } + + +/** +Loads schedule settings from CenRep + +@param aRepository +CenRep repository to load settings from +*/ +EXPORT_C void CMsvScheduleSend::LoadScheduleSettingsL(CRepository& aRepository) + { + TMsvScheduleSettingsUtils::LoadScheduleSettingsL(*iSettings, aRepository); + TMsvScheduleSettingsUtils::LoadOffPeakSettingsL(*iOffPeakTimes, aRepository); + TMsvScheduleSettingsUtils::LoadSendErrorSettingsL(*iErrorActions, aRepository); + TMsvScheduleSettingsUtils::LoadSysAgentSettingsL(*iAgentActions, aRepository); + } + +/** +Verifies that the schedule information stored in specified messages is the +same as that on the task scheduler. + +@param aSelection +Array of message IDs that need to be checked against the task scheduler. + +@panic ScheduleSend-DLL 0 +The array of message IDs is empty. +Debug build only. +*/ +EXPORT_C void CMsvScheduleSend::CheckScheduleL(const CMsvEntrySelection& aSelection) + { + __ASSERT_DEBUG(aSelection.Count(), gPanic(EMessageSelectionEmpty)); + + GetMessagesL(aSelection); //Leaves with KErrNotFound if there are no messages returned in iSchEntries + TInt entryCount = iSchEntries->Count(); + SCHSENDLOG(FLog(_L8("Asked to check schedule for %d msgs"), entryCount)); + + ConnectAndRegisterL(); + + while (entryCount--) + { + TBool found = EFalse; + TTsTime schTime; + + CMsvScheduledEntry& sEntry = *iSchEntries->At(entryCount); + + if (!sEntry.iData.IsReset()) + { + TSchedulerItemRef ref; + TInt size = 0; + TTaskInfo info; + + TInt err = iScheduler.GetTaskDataSize(sEntry.iData.iTaskId, size); + + if (!err) + { + HBufC* buf = HBufC::NewLC(size); + TPtr ptr = buf->Des(); + + User::LeaveIfError(iScheduler.GetTaskInfoL(sEntry.iData.iTaskId, info, ptr, ref, schTime)); + + CleanupStack::PopAndDestroy(buf); + found = ETrue; + } + else if (err != KErrNotFound) + { + User::Leave(err); + } + } + + if (iServerEntry.SetEntry(sEntry.Id()) == KErrNone) + { + TMsvEntry entry = iServerEntry.Entry(); + TInt sendingState = entry.SendingState(); + + if (sendingState == KMsvSendStateScheduled || sendingState == KMsvSendStateResend || entry.Scheduled()) + { + if (found) + { + entry.SetScheduled(ETrue); + entry.iDate = schTime.GetUtcTime(); + User::LeaveIfError(iServerEntry.ChangeEntry(entry)); + } + else + { + entry.SetScheduled(EFalse); + entry.SetSendingState(KMsvSendStateUnknown); + entry.iDate.UniversalTime(); + User::LeaveIfError(iServerEntry.ChangeEntry(entry)); + SendingCompleteL(sEntry, EFalse); + } + } + } + } + } + +/** +Determines the schedule time for a message. + +The schedule time is determined by the error action. If no error action is +supplied, then the error action is obtained from the iErrorActions. This is a +list of error codes associated with an error action. The error code with which +the message failed to be sent is used to determine the error action from this +list. + +This function also increments the number of re-tries that the message has +undergone. + +A value of ETrue is returned if the message can be sent or a value of EFalse if +the message cannot be sent. + +If the message can be sent, then its schedule date and pending conditions flag +are updated. + +@see CMsvScheduleSend::ReScheduleL(). + +@param aMessage +The message to be re-scheduled. + +@param aFromTime +The current time. Used to calculate the absolute schedule time once the interval +has been established. + +@param aErrorAction +The error action applied to the message. This can be NULL in which case the error +action is obtained from iErrorActions. + +@return +A value of ETrue if the message should be sent or EFalse if it should not +*/ +TBool CMsvScheduleSend::SetMessageStartTime(CMsvScheduledEntry& aMessage, const TTime& aFromTime, const TMsvSendErrorAction* aErrorAction) + { + TBool sendMsg = EFalse; + TMsvSendErrorAction action; + + if( aErrorAction != NULL ) + { + sendMsg = aMessage.CanSendToAnyRecipients(*aErrorAction); + action = *aErrorAction; + } + else + { + sendMsg = aMessage.CanSendToAnyRecipients(*iErrorActions, action); + } + + SCHSENDLOG(FLog(_L8("\t\tCanSendToAnyRecipients() ret %d"), sendMsg)); + + if( sendMsg ) + { + // Increase the number of times this message has been retried. + if( action.iRetries != ESendRetriesInfinite ) + { + aMessage.iData.IncreaseRetries(); + aMessage.RecipientsIncreaseRetries(); + } + + TBool retryConditionMet = action.iAction == ESendActionRetryConditionMet; + + if( retryConditionMet && aMessage.PendingConditions() ) + { + // There are two cases in which the message being re-scheduled has + // already been scheduled for pending conditions met and the error + // action is retry when conditions met. + // 1. The timeout has expired - current time exceeds the message's + // schedule time (assuming that the message has a timeout). + // 2. The conditions were temporarily met and by the time the + // waiting message tried to be sent the conditions were no + // longer true. + if( aMessage.ScheduleDate()!= Time::MaxTTime() && aFromTime > aMessage.ScheduleDate() ) + { + // Case 1 - the message should not be re-scheduled (this marks + // the message as failed). + sendMsg = EFalse; + } + // NOTE - the else is case 2; the message should be re-scheduled, + // but the timeout value should NOT be re-calculated -> do nothing. + } + else + { + TTimeIntervalSeconds interval; + if( GetNextRetry(aMessage, action, interval) ) + { + if( interval.Int() == 0 && retryConditionMet ) + { + // The timeout was set to zero - set a schedule time of + // Time::MaxTTime(). + aMessage.SetScheduleDate(Time::MaxTTime()); + } + else + { + TTime schTime = aFromTime + interval; + RoundUpToMinute(schTime); + aMessage.SetScheduleDate(schTime); + } + aMessage.SetPendingConditions(retryConditionMet); + } + else + { + sendMsg = EFalse; + } + + SCHSENDLOG(FLog(_L8("\t\tGetNextRetry() ret %d"), sendMsg)); + } + } + + if( !sendMsg ) + { + TBool failed = ETrue; + TInt state = KMsvSendStateFailed; + + if( aMessage.RecipientsAllSent() ) + { + failed = EFalse; + state = KMsvSendStateSent; + } + else + { + aMessage.RecipientsSetFailed(); + } + + aMessage.SetFailed(failed); + aMessage.SetSendingState(state); + } + + return sendMsg; + } + +/** +Utility function that rounds a specified time up to the nearest minute. + +@param aTime +On return, the passed value rounded up to the nearest minute. +*/ + +EXPORT_C void CMsvScheduleSend::RoundUpToMinute(TTime& aTime) + { + TDateTime dt(aTime.DateTime()); + + if (dt.MicroSecond() != 0 || dt.Second() != 0) + { + dt.SetMicroSecond(0); + dt.SetSecond(0); + aTime = dt; + aTime += (TTimeIntervalMinutes) 1; + } + } + +/* + Groups messages in aSchEntries by the date (time) they are to be scheduled + and if they are pending conditions met or not and then schedules each group. +*/ + +void CMsvScheduleSend::DoReScheduleL(CMsvScheduledEntries& aSchEntries) + { + const TInt entryCount = aSchEntries.Count(); + __ASSERT_DEBUG(entryCount > 0, gPanic(EMessageSelectionEmpty)); + + // Sort aSchEntries by CMsvScheduledEntry::iEntry.iDate and if they are + // pending conditions or not. + SortByDateAndPendingConditionsL(aSchEntries); + + //New selection of messages that will be used to store messages + //that are to be sent at the same time. + CMsvScheduledEntries* sel = new (ELeave) CMsvScheduledEntries(KMsvSchsendArrayGrowth); + CleanupStack::PushL(sel); + + sel->SetReserveL(entryCount); // so following AppendL()s won't leave + + CMsvScheduledEntry* curMessage = aSchEntries[0]; + TTime lastTime = curMessage->ScheduleDate(); + TBool lastPending = curMessage->PendingConditions(); + sel->AppendL(curMessage); + + // Delete old schedule + DeleteScheduleForEntryL(*curMessage); + + for( TInt curMsg = 1; curMsg < entryCount; ++curMsg ) // must forward traverse the array + { + curMessage = aSchEntries[curMsg]; + TTime thisTime = curMessage->ScheduleDate(); + TBool thisPending = curMessage->PendingConditions(); + + // Delete old schedule + DeleteScheduleForEntryL(*curMessage); + if( thisTime == lastTime && lastPending == thisPending ) + { + sel->AppendL(curMessage); + } + else + { + // Schedule the messages in the selection + DoScheduleL(*sel, KMsvSendStateResend, lastTime, lastPending); + sel->Reset(); // not ResetandDestroy because the messages are still in schEntries + sel->AppendL(curMessage); + lastTime = thisTime; + lastPending = thisPending; + } + } + + if( sel->Count() ) + { + // Schedule the remaining messages + DoScheduleL(*sel, KMsvSendStateResend, lastTime, lastPending); + } + + sel->Reset(); // not ResetandDestroy because the messages are still in schEntries + CleanupStack::PopAndDestroy(sel); + } + +/* + Gets the Message ID stored against the task scheduler task aTaskId + + @param aTaskId ID of task scheduler task +*/ + +TMsvId CMsvScheduleSend::GetMessageIdForTaskL(TInt aTaskId) + { + TInt taskSize = 0; + TTaskInfo taskInfo; + TSchedulerItemRef taskSch; + TTsTime taskDue; + + User::LeaveIfError(iScheduler.GetTaskDataSize(aTaskId, taskSize)); + + HBufC* taskData = HBufC::NewLC(taskSize); + TPtr ptr(taskData->Des()); + + User::LeaveIfError(iScheduler.GetTaskInfoL(aTaskId, taskInfo, ptr, taskSch, taskDue)); + + //Restore the TMsvSchedulePackage stored against the task scheduler task + TMsvSchedulePackage taskPackage; + taskPackage.UnpackL(taskInfo, *taskData); + + CleanupStack::PopAndDestroy(taskData); + + return taskPackage.iId; + } + +/* + Checks whether the message ID stored against task scheduler task aMessage.iData.iTaskId equals aMessage.Id(). +*/ + +TBool CMsvScheduleSend::TaskAndMessageMatchL(const CMsvScheduledEntry& aMessage) + { + TMsvId id = 0; + TRAPD(err, id = GetMessageIdForTaskL(aMessage.iData.iTaskId)); + + SCHSENDLOG(FLog(_L8("\tGetMessageIdForTask [taskId=%d err=%d id=%d aMessage.Id=%d"), aMessage.iData.iTaskId, err, id, aMessage.Id())); + TBool match = EFalse; + + if (err == KErrNone) + { + match = (id == aMessage.Id()); + } + else if (err != KErrNotFound) + { + User::Leave(err); + } + + return match; + } + +void CMsvScheduleSend::ResetScheduleInfoForEntryL(CMsvScheduledEntry& aMessage, const TBool aChangeEntry) + { + // Reset the scheduling info in the entry. + TMsvId oldId = iServerEntry.Entry().Id(); + User::LeaveIfError(iServerEntry.SetEntry(aMessage.Id())); + SendingCompleteL(aMessage, aChangeEntry); + iServerEntry.SetEntry(oldId); // ignore any error + } + +void CMsvScheduleSend::DeleteScheduleForEntryL(CMsvScheduledEntry& aMessage) + { + //Connect and register with the task scheduler + //Delete the task from the task scheduler + ConnectAndRegisterL(); + + if (TaskAndMessageMatchL(aMessage)) + { + TInt err = iScheduler.DeleteTask(aMessage.iData.iTaskId); + + SCHSENDLOG(FLog(_L8("\tDeleteTask Task=%d, Err=%d"), aMessage.iData.iTaskId, err)); + + if (err != KErrNotFound) + User::LeaveIfError(err); + + //Delete the schedule if there are no more tasks assigned to it + + //Declare variable to pass into GetScheduleL() + TScheduleState2 schState; + TTsTime schDueTime; + + // Get the schedule Type + TScheduleType scheduleType; + iScheduler.GetScheduleTypeL(aMessage.iData.iRef.iHandle, scheduleType); + iSchTaskInfo->Reset(); + + // Depends on the Schedule Type + if (scheduleType == ETimeSchedule) + { + //Get details of the existing time schedule + err = iScheduler.GetScheduleL(aMessage.iData.iRef.iHandle, schState, *iSchEntryInfo, *iSchTaskInfo, schDueTime); + + SCHSENDLOG(FLog(_L8("\tGetScheduleL Sch=%d, Err=%d"), aMessage.iData.iRef.iHandle, err)); + } + + else if (scheduleType == EConditionSchedule) + { + CArrayFixFlat* schConditions = + new (ELeave) CArrayFixFlat(KMsvSchsendArrayGrowth); + + CleanupStack::PushL(schConditions); + //Get details of the existing condition schedule + err = iScheduler.GetScheduleL(aMessage.iData.iRef.iHandle, schState, *schConditions, schDueTime, *iSchTaskInfo); + + SCHSENDLOG(FLog(_L8("\tGetScheduleL Sch=%d, Err=%d"), aMessage.iData.iRef.iHandle, err)); + + CleanupStack::PopAndDestroy(schConditions); + + } + else + { + User::Leave(KErrNotFound); + } + + if (!err) + { + if (iSchTaskInfo->Count() == 0) + { + SCHSENDLOG(FLog(_L8("\tDeleting schedule %d"), aMessage.iData.iRef.iHandle)); + iScheduler.DeleteSchedule(aMessage.iData.iRef.iHandle); //ignore error + } + } + else if (err != KErrNotFound) + { + User::Leave(err); + } + } + } + +/** +Determines whether the message should be sent and if so the time interval (in +seconds) that must elapse before aMessage should be sent. + +The time interval is determined by the error action supplied. In the case of an +error action of ESendActionRetryConditionMet, the time interval indicates how +long the message can be pending conditions to be met before the message is failed. + +The actual time period is defined in the iSettings object. This will be either +iPendingConditionsTimeout, iShortInterval, iLongInterval or an element of +iVariableIntervals. + +If the message should be sent then a value of ETrue is returned. A value of +EFalse is returned if the message should not be sent. In this case the output +argument aInterval is not valid. + +@param aMessage +The message to be re-scheduled. + +@param aErrorAction +The error action that determines the re-schedule behaviour. + +@param aInterval +An output argument that holds the time interval to when the message should be +re-sent or when the message should be failed. + +@return +A value of ETrue if the message should be sent or EFalse if it should not +*/ +TBool CMsvScheduleSend::GetNextRetry(CMsvScheduledEntry& aMessage, const TMsvSendErrorAction& aErrorAction, TTimeIntervalSeconds& aInterval) const + { + TBool retVal = (aErrorAction.iAction != ESendActionFail); + aInterval = 0; + + if( retVal ) + { + if( aErrorAction.iAction == ESendActionRetryConditionMet ) + { + // Interval given by the agent actions timeout value. + aInterval = iSettings->PendingConditionsTimeout().Int() * 60; + } + else if( aErrorAction.iAction == ESendActionRetryImmediately ) + { + aInterval = iSettings->ShortInterval(); + } + else + { + if( aErrorAction.iRetrySpacing == ESendRetrySpacingVariable ) + { + //Retrieve the variable spacing associated with the retry count + const CArrayFixFlat& varIntervals = iSettings->VariableIntervals(); + const TInt count = varIntervals.Count(); + const TInt retry = aMessage.iData.Retries(); + + if( count == 0 ) + { + aInterval = iSettings->LongInterval(); + } + else if( retry < count ) + { + aInterval = varIntervals[retry]; + } + else + { + aInterval = varIntervals[count - 1]; + } + } + else + { + // Got here then aScheduleAction.iRetrySpacing == EStatic. + aInterval = iSettings->LongInterval(); + } + + __ASSERT_DEBUG(aInterval.Int() > 0, gPanic(ERetryIntervalMustByPositive)); + } + } + return retVal; + } + +/** +Searches the scheduler for an existing schedule item with a schedule time that +matches with time supplied. + +@see RScheduler::GetScheduleL() + +@param aScheduler +Handle to the scheduler. + +@param aStartTime +Schedule start time. + +@param aRef +On return, the schedule item. + +@leave KErrNotFound +No schedule found matching the schedule time. +*/ +EXPORT_C void CMsvScheduleSend::FindScheduleL(RScheduler& aScheduler, const TTime& aStartTime, TSchedulerItemRef& aRef) + { + CArrayFixFlat* entryInfos = new (ELeave) CArrayFixFlat(1); + CleanupStack::PushL(entryInfos); + + CArrayFixFlat* taskInfos = new (ELeave) CArrayFixFlat(1); + CleanupStack::PushL(taskInfos); + + aRef.iHandle = KErrNotFound; + + CArrayFixFlat* refs = new (ELeave) CArrayFixFlat(KMsvSchsendArrayGrowth); + CleanupStack::PushL(refs); + + User::LeaveIfError(aScheduler.GetTaskRefsL(*refs, EAllSchedules, EMyTasks)); + + TInt count = refs->Count(); + TScheduleState2 state; + TTsTime nextDue; + + while (count-- && aRef.iHandle == KErrNotFound) + { + const TSchedulerItemRef& tempRef = (*refs)[count]; + + TScheduleType type; + User::LeaveIfError(aScheduler.GetScheduleTypeL(tempRef.iHandle, type)); + if( type == ETimeSchedule ) + { + entryInfos->Reset(); + taskInfos->Reset(); + const TInt err = aScheduler.GetScheduleL(tempRef.iHandle, state, *entryInfos, *taskInfos, nextDue); + if( err == KErrNone && nextDue.GetUtcTime() == aStartTime ) + aRef = tempRef; + } + } + + CleanupStack::PopAndDestroy(3, entryInfos); + if (aRef.iHandle == KErrNotFound) + User::Leave(KErrNotFound); + } + +/** +Searches the scheduler for an existing conditions schedule item with a set of +pending conditions and timeout value that matches with those supplied. + +@see RScheduler::GetScheduleL + +@param aScheduler +Handle to the scheduler. + +@param aConditions +The set of System Agent conditions that are required to be met to trigger the +schedule. + +@param aTimeout +The timeout value for the schedule. + +@param aRef +On return, the schedule item. + +@leave KErrNotFound +No schedule found matching the schedule conditions and timeout. +*/ +EXPORT_C void CMsvScheduleSend::FindScheduleL( + RScheduler& aScheduler, + const CArrayFixFlat& aConditions, + const TTime& aTimeout, + TSchedulerItemRef& aRef) + { + CArrayFixFlat* schConditions = new (ELeave) CArrayFixFlat(KMsvSchsendArrayGrowth); + CleanupStack::PushL(schConditions); + + CArrayFixFlat* taskInfos = new (ELeave) CArrayFixFlat(1); + CleanupStack::PushL(taskInfos); + + aRef.iHandle = KErrNotFound; + + CArrayFixFlat* refs = new (ELeave) CArrayFixFlat(KMsvSchsendArrayGrowth); + CleanupStack::PushL(refs); + + User::LeaveIfError(aScheduler.GetTaskRefsL(*refs, EAllSchedules, EMyTasks)); + + TInt count = refs->Count(); + TScheduleState2 state; + TTsTime nextTimeout; + + while( count-- && aRef.iHandle == KErrNotFound ) + { + const TSchedulerItemRef& tempRef = (*refs)[count]; + + TScheduleType type; + User::LeaveIfError(aScheduler.GetScheduleTypeL(tempRef.iHandle, type)); + if( type == EConditionSchedule ) + { + taskInfos->Reset(); + const TInt err = aScheduler.GetScheduleL(tempRef.iHandle, state, *schConditions, nextTimeout, *taskInfos); + if( err == KErrNone && nextTimeout.GetUtcTime() == aTimeout && *schConditions == aConditions ) + aRef = tempRef; + } + } + + CleanupStack::PopAndDestroy(3, schConditions); + if (aRef.iHandle == KErrNotFound) + User::Leave(KErrNotFound); + } + +void CMsvScheduleSend::FindScheduleL(const TTime& aTime, const CArrayFixFlat& aSchConditions, TBool aPendingConditions, TSchedulerItemRef& aRef) + { + if( aPendingConditions ) + FindScheduleL(iScheduler, aSchConditions, aTime, aRef); + else + FindScheduleL(iScheduler, aTime, aRef); + } + +/** +Does the actual scheduling of the supplied messages. + +This function is called by ScheduleL() and DoReScheduleL(). The supplied messages +are assumed to have the values for the following data - MTM UID, off-peak flag, +schedule date and pending conditions flag. In debug mode, if these info is +matching in all the messages then a panic will occur. + +@param aSchEntries +An array with the messages to be scheduled. + +@param aCommandId +The command id that must be used by the Send.Exe to eventually send the messages. + +@param aFinalState +The sending state to set to the messages to if the message is successfully +scheduled. + +@param aTime +For conditions-scheduled messages this is the timeout, for time-scheduled messages +this is the scheduled time. + +@param aPendingConditions +A flag indicating whether the schedule should be pending conditions. +*/ +void CMsvScheduleSend::DoScheduleL(CMsvScheduledEntries& aSchEntries, const TInt aFinalState, const TTime& aTime, TBool aPendingConditions) + { + __ASSERT_DEBUG(aSchEntries.Count(), gPanic(EMessageSelectionEmpty)); + +#ifdef _DEBUG + // Check that the mtm, time, off peak flag and pending conditions flag are + // the same for every message. + TInt count = aSchEntries.Count(); + CMsvScheduledEntry* schEntry = aSchEntries[0]; + + while (count--) + { + CMsvScheduledEntry* entry = aSchEntries[count]; + + __ASSERT_DEBUG(entry->Mtm() == schEntry->Mtm(), gPanic(EMessagesNotSameMtm)); + __ASSERT_DEBUG((entry->OffPeak() && schEntry->OffPeak()) || (!entry->OffPeak() && !schEntry->OffPeak()), gPanic(EMessagesNotSameOffPeak)); + __ASSERT_DEBUG(entry->ScheduleDate() == schEntry->ScheduleDate(), gPanic(EMessagesNotSameTime)); + __ASSERT_DEBUG(entry->PendingConditions() == schEntry->PendingConditions(), gPanic(EMessagesNotSamePendingConditions)); + schEntry = entry; + } +#endif + + // Connect and register with the task scheduler + ConnectAndRegisterL(); + + // Determine the start time and validity period + TTime latencyTime; + latencyTime.UniversalTime(); + latencyTime += iSettings->Latency(); + TTime startTime = latencyTime; + + if( aTime > startTime ) + { + // The schedule time is in the future or there is no schedule time + // (the messages are pending conditions) - update the start time. + startTime = aTime; + } + + TTimeIntervalMinutes valPeriod = iSettings->ValidityPeriod(); + CMsvScheduledEntry* firstMessage = aSchEntries[0]; + + // Determine the start time and validity period of the new schedule - this + // is not applicable if the messages are schedule for pending conditions. + if( firstMessage->OffPeak() && !aPendingConditions ) + { + GetOffPeakL(startTime, startTime, valPeriod); + } + + CArrayFixFlat* schConditions = new (ELeave) CArrayFixFlat(KMsvSchsendArrayGrowth); + CleanupStack::PushL(schConditions); + if( aPendingConditions ) + PopulateScheduleConditionsL(*schConditions); + + // Create a schedule + TSchedulerItemRef schItemRef; + TBool schFound = EFalse; + + // Check to see if a schedule already exists for this particular schedule. + // Need to consider both the start time and if the schedule is for pending + // conditions met. + // NOTE - if the schedule time is not in the future, then there is no need + // to search for an existing schedule. + if( startTime > latencyTime ) + { + if( startTime != Time::MaxTTime() ) + RoundUpToMinute(startTime); + + TRAPD(err,FindScheduleL(startTime, *schConditions, aPendingConditions, schItemRef)); + if( err == KErrNone ) + { + SCHSENDLOG(FLog(_L("\tFound schedule %d"), schItemRef.iHandle)); + schFound = ETrue; + } + // Else ignore the error and create and new schedule. + } + + if( !schFound ) + { + // The appropriate schedule. + CreateScheduleL(startTime, valPeriod, *schConditions, aPendingConditions, schItemRef); + } + + CleanupStack::PopAndDestroy(schConditions); + + // Disable the schedule so that it doesn't fire while scheduling + User::LeaveIfError(iScheduler.DisableSchedule(schItemRef.iHandle)); + + // Schedule the messages + const TInt messageCount = aSchEntries.Count(); + TInt schErr = KErrNone; + TInt curMsg; + + for( curMsg = 0; curMsg < messageCount; ++curMsg ) // must forward traverse the array + { + CMsvScheduledEntry* message = aSchEntries[curMsg]; + + message->SetScheduleDate(startTime); + TRAP(schErr, ScheduleEntryL(*message, aFinalState, startTime, schItemRef, aPendingConditions)); + + if( schErr != KErrNone ) + break; + } + + if( schErr != KErrNone && curMsg == 0 && !schFound ) + { + iScheduler.DeleteSchedule(schItemRef.iHandle); // ignore error + } + else + { + User::LeaveIfError(iScheduler.EnableSchedule(schItemRef.iHandle)); + } + + User::LeaveIfError(schErr); + } + +/** +Creates a new schedule on the task scheduler with which each message can be +associated. + +The schedule is triggered by a start time being reached. + +@see RScheduler::CreatePersistentSchedule + +@param aScheduler +Handle to scheduler to update. + +@param aSettings +Scheduler settings. + +@param aStartTime +Schedule start time. + +@param aValidityPeriod +Schedule validity period. + +@param aRef +On return, the new schedule. +*/ +EXPORT_C void CMsvScheduleSend::CreateScheduleL(RScheduler& aScheduler, const CMsvScheduleSettings& aSettings, const TTime& aStartTime, const TTimeIntervalMinutes& aValidityPeriod, TSchedulerItemRef& aRef) + { + //Determine the new schedule entry + TScheduleEntryInfo2 taskInfo; + taskInfo.SetIntervalType(aSettings.IntervalType()); + taskInfo.SetInterval(1); + TTsTime startTime(aStartTime,ETrue); + taskInfo.SetStartTime(startTime); + taskInfo.SetValidityPeriod(aValidityPeriod); + + //Create the TScheduleEntryInfo arrag that is required by CreatePersistentSchedule + CArrayFixFlat* entryInfos = new (ELeave) CArrayFixFlat(1); + CleanupStack::PushL(entryInfos); + + //Append the new schedule entry + entryInfos->AppendL(taskInfo); + + //Create the schedule + User::LeaveIfError(aScheduler.CreatePersistentSchedule(aRef, *entryInfos)); + + CleanupStack::PopAndDestroy(entryInfos); + } + +/** +Creates a new schedule on the task scheduler with which each message can be +associated. + +The schedule is triggered by either a set of conditions being met or a timeout +being reached. + +@see RScheduler::CreatePersistentSchedule +@see TSysAgentCondition + +@param aScheduler +Handle to scheduler to update. + +@param aConditions +The set of System Agent conditions that are required to be met to trigger the +schedule. + +@param aTimeout +The timeout value for the schedule. + +@param aRef +On return, the new schedule. +*/ +EXPORT_C void CMsvScheduleSend::CreateScheduleL( + RScheduler& aScheduler, + const CArrayFixFlat& aConditions, + const TTime& aTimeout, + TSchedulerItemRef& aRef) + { + TTsTime timeout(aTimeout,ETrue); + User::LeaveIfError(aScheduler.CreatePersistentSchedule(aRef, aConditions, timeout)); + } + +void CMsvScheduleSend::CreateScheduleL(const TTime& aTime, const TTimeIntervalMinutes& aValidityPeriod, const CArrayFixFlat& aSchConditions, TBool aPendingConditions, TSchedulerItemRef& aRef) + { + if( aPendingConditions ) + { + CreateScheduleL(iScheduler, aSchConditions, aTime, aRef); + #ifndef _MSG_NO_LOGGING + TBuf<32> bufDate; + aTime.FormatL(bufDate, _L("%D%M%Y%/0%1%/1%2%/2%3%/3 %-B%:0%J%:1%T%:2%S%.%*C4%:3%+B")); + SCHSENDLOG(FLog(_L("\tCreated Schedule %d for pending %d conditions or %S"), aRef.iHandle, aSchConditions.Count(), &bufDate)); + #endif + } + else + { + CreateScheduleL(iScheduler, *iSettings, aTime, aValidityPeriod, aRef); + #ifndef _MSG_NO_LOGGING + TBuf<32> bufDate; + aTime.FormatL(bufDate, _L("%D%M%Y%/0%1%/1%2%/2%3%/3 %-B%:0%J%:1%T%:2%S%.%*C4%:3%+B")); + SCHSENDLOG(FLog(_L("\tCreated Schedule %d for %S"), aRef.iHandle, &bufDate)); + #endif + } + } + +/** +Adds an entry to an existing schedule. + +@see RScheduler::ScheduleTask() + +@param aScheduler +Scheduler to access. + +@param aRef +Id of the schedule. + +@param aPackage +Scheduler settings. + +@param aInfo +Information about the entry to be added to the schedule. + +@leave KErrNotFound +No existing schedule with the specified Id. +*/ +EXPORT_C void CMsvScheduleSend::ScheduleEntryL(RScheduler& aScheduler, const TSchedulerItemRef& aRef, const TMsvSchedulePackage& aPackage, TTaskInfo& aInfo) + { + aInfo.iPriority = EDefaultTaskPriority; + aInfo.iRepeat = EDefaultTaskRepeat; + + HBufC* hBuf = NULL; + aPackage.PackLC(aInfo, hBuf); + + //Schedule the task + User::LeaveIfError(aScheduler.ScheduleTask(aInfo, *hBuf, aRef.iHandle)); + + CleanupStack::PopAndDestroy(hBuf); + } + +/** +Schedules aMessage on the schedule referred to by aRef. + +Updates the message entry and the schedule data using the utility function +UpdateEntryAfterSchedule(). + +@see CMsvScheduleSend::UpdateEntryAfterSchedule + +@param aMessage +The message to be scheduled. + +@param aFinalState +The sending state to set to the messages to if the message is successfully +scheduled. + +@param aFromTime +The time to schedule the messages for sending. + +@param aRef +The ID of the schedule to add this task to. + +@param aPendingConditions +A flag indicating whether this message is schedule for conditions. +*/ +void CMsvScheduleSend::ScheduleEntryL(CMsvScheduledEntry& aMessage, const TInt aFinalState, const TTime& aStartTime, const TSchedulerItemRef& aRef, TBool aPendingConditions) + { + //Create a new task to associate with the schedule + TTaskInfo taskInfo; + iPackage.iId = aMessage.Id(); + + ScheduleEntryL(iScheduler, aRef, iPackage, taskInfo); + + // Change the scheduled flag and sending state of the message + if( iServerEntry.SetEntry(aMessage.Id()) == KErrNone ) + { + TMsvEntry entry(iServerEntry.Entry()); + + aMessage.Entry(entry); + UpdateEntryAfterSchedule(aRef, taskInfo, aStartTime, aFinalState, entry, aMessage.iData); + entry.SetPendingConditions(aPendingConditions); + + SCHSENDLOG(FLog(_L8("\t\tScheduled msg %d (Mtm=%d, State=%d, Sch=%d, Task=%d, Pending=%d)"), entry.Id(), entry.iMtm.iUid, aFinalState, aMessage.iData.iRef.iHandle, aMessage.iData.iTaskId, entry.PendingConditions())); + + User::LeaveIfError(iServerEntry.ChangeEntry(entry)); + + //Store the message data + CMsvStore* store = iServerEntry.EditStoreL(); + CleanupStack::PushL(store); + aMessage.StoreL(*store); + store->CommitL(); + CleanupStack::PopAndDestroy(store); + iServerEntry.SetEntry(KMsvNullIndexEntryId); + } + } + +/** +Utility function that updates message index entry fields to reflect +the message's scheduling. + +@param aRef +Scheduler item. + +@param aInfo +Scheduler task information. + +@param aTime +Schedule start time. + +@param aFinalState +Sending state flag. + +@param aEntry +On return, updated index entry. + +@param aData +On return, populated messaging scheduling data. +*/ +EXPORT_C void CMsvScheduleSend::UpdateEntryAfterSchedule(const TSchedulerItemRef& aRef, const TTaskInfo& aInfo, const TTime& aTime, TInt aFinalState, TMsvEntry& aEntry, TMsvEntryScheduleData& aData) + { + aEntry.SetScheduled(ETrue); + aEntry.SetFailed(EFalse); + aEntry.SetSendingState(aFinalState); + aEntry.SetConnected(EFalse); + aEntry.iDate = aTime; + aData.iRef = aRef; + aData.iTaskId = aInfo.iTaskId; + } + +/** +Sorts the supplied array of messages. + +The messages are sorted by their schedule date (held in the iEntry.iDate member +of CMsvScheduledEntry). Messages with the same schedule time are then further +sorted by if they to be scheduled for pending conditions. + +@param aSchEntries +The input/output parameter that holds the messages to be sorted. This will +contain the sorted list once the function completes. +*/ +void CMsvScheduleSend::SortByDateAndPendingConditionsL(CMsvScheduledEntries& aSchEntries) + { + //TO DO: Test this function!!! + + TInt count = aSchEntries.Count(); + CMsvScheduledEntry* curEntry = NULL; + CMsvScheduledEntry* compEntry = NULL; + + for (TInt i = 0; i < count - 1; ++i) + { + curEntry = aSchEntries[i]; + + for (TInt j = i + 1; j < count; ++j) + { + compEntry = aSchEntries[j]; + + //Compare the dates of the two entries + if( curEntry->ScheduleDate() > compEntry->ScheduleDate() || + (curEntry->ScheduleDate() == compEntry->ScheduleDate() && + curEntry->PendingConditions() && !compEntry->PendingConditions()) ) + { + //Swap them + aSchEntries.Delete(i); + aSchEntries.InsertL(i, compEntry); + aSchEntries.Delete(j); + aSchEntries.InsertL(j, curEntry); + } + } + } + } + +void CMsvScheduleSend::GetMessagesL(const CMsvEntrySelection& aSelection) + { + iSchEntries->ResetAndDestroy(); + + const TInt entryCount = aSelection.Count(); + + iSchEntries->SetReserveL(entryCount); //so following AppendL()s won't leave + + CMsvScheduledEntry* schEntry = NULL; + + for (TInt curMsg = 0; curMsg < entryCount; ++curMsg) //not while because must transverse forward + { + //Retrieve each message from the message server + TMsvId msvId = aSelection.At(curMsg); + + TRAPD(error, schEntry = GetMessageL(msvId)); + + if (error == KErrNone) + { + CleanupStack::PushL(schEntry); + iSchEntries->AppendL(schEntry); + CleanupStack::Pop(schEntry); + } + else if (error != KErrLocked && error != KErrNotFound) + { + User::Leave(error); + } + } + } + +/** +Connects to and registers with the task scheduler. + +@param aScheduler +Handle to the scheduler to connect to. + +@param aSettings +Schedule settings. + +@leave Any error code +RScheduler::Connect() returned an error. + +@leave Any error code +RFs::Connect() returned an error. + +@leave KErrPathNotFound +The .EXE file to be called by the Task Scheduler when messages are due to +be sent cannot be found. The filename is part of the schedule settings. + +@leave Any error code +RScheduler::Register() returned an error. +*/ + +EXPORT_C void CMsvScheduleSend::ConnectAndRegisterL(RScheduler& aScheduler, const CMsvScheduleSettings& aSettings) + { + //Connect to the task scheduler + User::LeaveIfError(aScheduler.Connect()); + + // Register the schsendexe.exe as the executable to run when the schedule + // fires. + User::LeaveIfError(aScheduler.Register(KSchSendExe(), aSettings.Priority())); + } + + +void CMsvScheduleSend::ConnectAndRegisterL() + { + //Check to see whether already registered. + if (iRegistered) + { + return; + } + + ConnectAndRegisterL(iScheduler, *iSettings); + + //No errors, so set iRegistered to true. + iRegistered = ETrue; + } + +/* + Retrieves the system off-peak times and + updates aStartTime and aValidityPeriod +*/ + +void CMsvScheduleSend::GetOffPeakL(TTime aFromTime, TTime& aStartTime, TTimeIntervalMinutes& aValidityPeriod) const + { + __ASSERT_DEBUG(iOffPeakTimes->Count() > 0, gPanic(EOffPeakTimesEmpty)); + + TMsvOffPeakTime opTime; + + TTime offPeakStart; + User::LeaveIfError(iOffPeakTimes->GetNextOffPeakTime(aFromTime, opTime, offPeakStart)); + + aValidityPeriod = opTime.ValidityPeriod(); + + if (offPeakStart < aFromTime) + { //aFromTime is within an off peak time + aStartTime = aFromTime; + + TTimeIntervalMinutes mins; + aStartTime.MinutesFrom(offPeakStart, mins); + TInt minsInt = mins.Int(); + + __ASSERT_DEBUG(minsInt >= 0, gPanic(EInvalidValidityPeriod)); + + aValidityPeriod = opTime.ValidityPeriod(); + + aValidityPeriod = (TTimeIntervalMinutes) (aValidityPeriod.Int() - minsInt); + } + else + { + aStartTime = offPeakStart; + } + + //To Do: Remove next line after testing + __ASSERT_DEBUG(aValidityPeriod.Int() > 0, gPanic(EInvalidValidityPeriod)); + } + +void CMsvScheduleSend::PopulateScheduleConditionsL(CArrayFixFlat& aSchConditions) + { + aSchConditions.Reset(); + + TInt count = iAgentActions->Count(); + TTaskSchedulerCondition condition; + + // All system agent condition have a UID of KUidSystemCategory. + condition.iCategory = KUidSystemCategory; + + for( TInt i = 0; i < count; ++i ) + { + + TMsvCondition agentCondition = iAgentActions->At(i).iCondition; + + condition.iKey = agentCondition.iVariable.iUid; + condition.iState = agentCondition.iState; + + switch( agentCondition.iType ) + { + case TMsvCondition::EMsvSchSendEquals: + condition.iType = TTaskSchedulerCondition::EEquals; + break; + case TMsvCondition::EMsvSchSendNotEquals: + condition.iType = TTaskSchedulerCondition::ENotEquals; + break; + case TMsvCondition::EMsvSchSendGreaterThan: + condition.iType = TTaskSchedulerCondition::EGreaterThan; + break; + case TMsvCondition::EMsvSchSendLessThan: + condition.iType = TTaskSchedulerCondition::ELessThan; + break; + default: + User::Invariant(); + break; + } + + aSchConditions.AppendL(condition); + } + } + +_LIT(KScheduleSendPanic, "ScheduleSend-DLL"); + +GLDEF_C void gPanic(TScheduleSendPanic aPanic) + { + User::Panic(KScheduleSendPanic,aPanic); + } + + +#ifndef _MSG_NO_LOGGING +void CMsvScheduleSend::FLog(TRefByValue aFormat, ...) + { + VA_LIST list; + VA_START(list, aFormat); + RFileLogger::WriteFormat(KSchSendLogDir, KSchSendLogFile, EFileLoggingModeAppend, + aFormat, list); + } + +void CMsvScheduleSend::FLog(TRefByValue aFormat, ...) + { + VA_LIST list; + VA_START(list, aFormat); + RFileLogger::WriteFormat(KSchSendLogDir, KSchSendLogFile, EFileLoggingModeAppend, + aFormat, list); + } +#endif