commonappservices/alarmserver/Server/Source/ASSrvAlarm.cpp
changeset 0 2e3d3ce01487
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/commonappservices/alarmserver/Server/Source/ASSrvAlarm.cpp	Tue Feb 02 10:12:00 2010 +0200
@@ -0,0 +1,1301 @@
+// 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:
+//
+
+#include "ASSrvAlarm.h"
+
+// User includes
+#include "ASSrvTimer.h"
+#include "ASSrvDataPool.h"
+#include "ASSrvAlarmQueue.h"
+#include "ASSrvStaticUtils.h"
+#include "ASSrvSoundSettings.h"
+#include "ASSrvServerWideData.h"
+#include "ASSrvAlarmSoundDetails.h"
+#include "ASSrvIteratorByState.h"
+#include "ASSrvSoundController.h"
+#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS
+#include "ASShdAlarm_internal.h"
+#endif
+
+
+// Definitions
+//#define __DEBUGGING_TIMES
+
+
+//
+// ----> TASSrvAlarm (source)
+//
+
+//*************************************************************************************
+/**
+ * Constructor
+ */
+TASSrvAlarm::TASSrvAlarm(CASSrvServerWideData& aData)
+:	iSoundPeriodCycleNumber(KUndefinedCycleIndex), iStartTimeForSoundCalculations(Time::NullTTime()),
+	iSoundState(ESoundStatePlayingNothing), iInternalServerFlags(0),
+	iOriginatingSessionId(KErrNotFound), iServerWideData(aData)
+	{
+	}
+
+
+//
+//
+//
+
+
+//*************************************************************************************
+/**
+ * Copy operator. Only copies certain data members. Possibly this
+ * should be removed...
+ */
+TASSrvAlarm& TASSrvAlarm::operator=(const TASSrvAlarm& aAlarm)
+	{
+	TASShdAlarm::operator=(aAlarm);
+	//
+	iSoundPeriodCycleNumber = aAlarm.iSoundPeriodCycleNumber;
+	iStartTimeForSoundCalculations = aAlarm.iStartTimeForSoundCalculations;
+	iSoundState = aAlarm.iSoundState;
+	iInternalServerFlags = aAlarm.iInternalServerFlags;
+	iOriginatingSessionId = aAlarm.iOriginatingSessionId;
+	iNotificationMessage = aAlarm.iNotificationMessage;
+	//
+	return *this;
+	}
+
+
+//
+//
+//
+
+
+//*************************************************************************************
+/**
+ * Change the status of this alarm, i.e. enabled, disabled
+ *
+ * @return  TInt KErrNone on success, or one of the system wide standard error codes.
+ *				 KErrLocked is returned if this alarm is a "workday" alarm but no valid
+ *				 workdays have been defined.
+ */
+TInt TASSrvAlarm::SetStatus(TAlarmStatus aStatus)
+	{
+	return doSetStatus(aStatus, EFalse);
+	}
+
+
+/**
+Change the state of this alarm.
+*/
+void TASSrvAlarm::SetState(TAlarmState aState)
+	{
+	const TAlarmState oldState = State();
+	
+	// Only process a change in state.
+	if (oldState == aState)
+		{
+		return;
+		}
+
+	// Update current state.
+	iState = aState;
+
+	// If a paused alarm has re-notified (after the pause period is up) or has
+	// been dismissed then clear the paused flag.
+	if (HasSoundPaused() || iState == EAlarmStateNotifying || iState == EAlarmStateNotified)
+		{
+		ClearSoundPausedFlag();
+		}
+
+	// Handle new state.
+	switch(aState)
+		{
+	case EAlarmStateQueued:
+	case EAlarmStateSnoozed:
+	case EAlarmStateWaitingToNotify:
+	case EAlarmStateInPreparation:
+		break;
+
+	case EAlarmStateNotified:
+		{		
+		// If the alarm does not repeat or it is a 24 hour alarm then we do
+		// nothing.  Notified alarms are cleaned up by the queue when they are
+		// more than 7 days old.
+		if (RepeatDefinition() != EAlarmRepeatDefintionRepeatOnce && RepeatDefinition() != EAlarmRepeatDefintionRepeatNext24Hours)
+			{
+			// Work out when the alarm should next repeat.
+			PrepareForNextRepeat();
+			}
+		}
+		break;
+
+	case EAlarmStateNotifying:
+		{
+		TTime now(ASSrvStaticUtils::UtcTimeNow());
+		ReinitializeSoundState(now);
+		}
+		break;
+
+	default:
+		{
+		__ASSERT_DEBUG(EFalse, ASSrvStaticUtils::Fault(ASSrvStaticUtils::EASSrvFaultAlarmStateNotHandled));
+		}
+		break;
+		}
+
+	// Notify queue so that it can work out if it needs to do some reordering.
+	// Only do this if this alarm has been queued (which we can infer if this
+	// alarm has a non-null identifier).
+	if (Id() != KNullAlarmId)
+		{
+		ServerData().Queue().HandleAlarmStateChanged(Id(), oldState);
+		}
+
+	// We call ourselves recursively if we are a repeating alarm.  We must do
+	// this *after* the above HandleAlarmStateChanged() call because there must
+	// be two state change notifictions otherwise observers will not behave
+	// correctly...
+	switch (RepeatDefinition())
+		{
+	case EAlarmRepeatDefintionRepeatOnce:
+	case EAlarmRepeatDefintionRepeatNext24Hours:
+		break;
+
+	case EAlarmRepeatDefintionRepeatDaily:
+	case EAlarmRepeatDefintionRepeatWorkday:
+	case EAlarmRepeatDefintionRepeatWeekly:
+#ifdef SYMBIAN_ALARM_REPEAT_EXTENSIONS
+	case EAlarmRepeatDefinitionRepeatDailyOnGivenDays:
+#endif
+		if (oldState == EAlarmStateNotifying)
+			{
+			// Make sure we keep the snoozed state and not change to queued as
+			// in the non-repeating alarms.
+			if (aState == EAlarmStateSnoozed)
+				{
+				if (Id() != KNullAlarmId)
+					{
+					ServerData().Queue().HandleAlarmStateChanged(Id(), aState);						
+					}
+				}
+			else
+				{
+				SetState(EAlarmStateQueued);					
+				}
+			}
+		break;
+
+	default:
+		break;
+		}
+	}
+
+//*************************************************************************************
+/**
+ * Change the characteristics of this alarm
+ */
+void TASSrvAlarm::SetCharacteristicsL(TAlarmCharacteristicsFlags aFlags, TASSrvSessionId aSessionChangingFlags)
+	{
+#ifdef SYMBIAN_SYSTEM_STATE_MANAGEMENT
+	// if it is a wakeup alarm and they are trying to set session specific then leave
+	if (aFlags.IsSet(EAlarmCharacteristicsSessionSpecific) && IsWakeup())
+		{
+		User::Leave(KErrArgument);		
+		}
+#endif
+	
+	const TAlarmCharacteristicsFlags oldCharacteristics = Characteristics();
+	if	(oldCharacteristics.Value() == aFlags.Value())
+		return;
+
+	// If it was session-specific, but it isn't anymore, then we need
+	// to complete the pending notification with KErrCancel
+	if	(oldCharacteristics.IsSet(EAlarmCharacteristicsSessionSpecific) &&
+		 !aFlags.IsSet(EAlarmCharacteristicsSessionSpecific))
+		{
+		// If there was a notification request then we complete it
+		NotificationMessageComplete(KErrCancel);
+
+		// Indicate that this alarm is no longer owned by a session
+		iFlags.Set(EASShdAlarmFlagsHasBecomeOrphaned);
+		}
+	else if (!oldCharacteristics.IsSet(EAlarmCharacteristicsSessionSpecific) &&
+		aFlags.IsSet(EAlarmCharacteristicsSessionSpecific))
+		{
+		// The session indicated wishes to take ownership of this alarm (but
+		// can't request a expiry notification because these can only be setup
+		// when an alarm is first created).
+		SetOriginatingSessionId(aSessionChangingFlags);
+
+		// Indicate that this alarm is now owned by a session
+		iFlags.Clear(EASShdAlarmFlagsHasBecomeOrphaned);
+		}
+
+	// Update state now
+	iCharacteristics = aFlags;
+
+	// Notify queue so that it can work out if it needs to do
+	// some re-ordering (only do this if we've been queued, and therefore
+	// have an alarm id).
+	if	(Id() != KNullAlarmId)
+		ServerData().Queue().HandleAlarmCharacteristicsChanged(Id(), oldCharacteristics);
+	}
+
+#ifdef SYMBIAN_SYSTEM_STATE_MANAGEMENT
+void TASSrvAlarm::SetWakeupAndNotifyQueueL(TBool aEnable)
+	{
+	if (IsWakeup() == aEnable)
+		{
+		return;
+		}
+
+	if (aEnable && iCharacteristics.IsSet(EAlarmCharacteristicsSessionSpecific))
+		{
+		// you cannot set a session alarm as wakeup
+		User::Leave(KErrArgument);		
+		}
+	
+	SetWakeup(aEnable);
+	if (Id() != KNullAlarmId)
+		{
+		ServerData().Queue().HandleWakeupChanged(Id());
+		}
+	}
+#endif
+
+#ifdef SYMBIAN_ALARM_REPEAT_EXTENSIONS
+void TASSrvAlarm::SetAlarmDaysL(TUint8 aAlarmDays)
+	{
+	// Check if any modification to current alarms days is required.
+	if (aAlarmDays == AlarmDays())
+		{
+		return;
+		}
+
+	User::LeaveIfError(SetAlarmDays(aAlarmDays));
+	User::LeaveIfError(ASSrvStaticUtils::ValidateAlarm(*this));
+	
+	// Notify queue so that it can work out if it needs to do some reordering.
+	ServerData().Queue().HandleAlarmDaysChanged(Id());
+	}
+#endif
+
+void TASSrvAlarm::SetAlarmOrphaned()
+	{
+	iFlags.Set(EASShdAlarmFlagsHasBecomeOrphaned);
+	}
+	
+//*************************************************************************************
+/**
+ * Set the originating session id for this alarm. The originating session Id is the
+ * session Id (a unique id allocated to each session) which is used to track
+ * all "session alarms" within the server.
+ */
+void TASSrvAlarm::SetOriginatingSessionId(TASSrvSessionId aId)
+	{
+	iOriginatingSessionId = aId;
+	if	(aId >= 0)
+		{
+		iFlags.Set(EASShdAlarmFlagsHasOwningSession);
+		}
+	else
+		{
+		iFlags.Clear(EASShdAlarmFlagsHasOwningSession);
+		}
+	}
+
+
+//
+//
+//
+
+
+/**
+Return the originating session id.
+
+@return The id of the session that created the TASSrvAlarm.
+*/
+TASSrvSessionId TASSrvAlarm::OriginatingSessionId() const
+	{
+	return iOriginatingSessionId;
+	}
+
+
+/**
+Return the alarm's sound state.
+
+@return The state of the alarm's sound.
+*/
+TASSrvAlarm::TSoundState TASSrvAlarm::SoundState() const
+	{
+	return iSoundState;
+	}
+
+
+//
+//
+//
+
+
+//*************************************************************************************
+/**
+ * Issue a request for notifications when this alarm expires
+ */
+void TASSrvAlarm::RequestExpiryNotificationL(const RMessage2& aMessage)
+	{
+	// Queue for notifications. This allows us to observe when the alarm expires
+	// and notify the client
+	ServerData().Timer().NotifyAlarmExpiredL(*this);
+
+	// Save a pointer
+	iNotificationMessage = aMessage;
+
+	// Update flags to indicate we have a pending notification message pointer
+	iInternalServerFlags.Set(EInternalServerFlagsNotifyPending);
+	}
+
+
+//*************************************************************************************
+/**
+ * Cancel a previous expiry notification request
+ */
+void TASSrvAlarm::RequestExpiryNotificationComplete(TInt aErrorCode)
+ 	{
+	NotificationMessageComplete(aErrorCode);	
+ 	}
+
+/**
+Return whether or not the alarm has a notification request pending.
+
+@return A TBool that is ETrue if the alarm has a notification request pending.
+*/
+TBool TASSrvAlarm::HasNotificationRequestPending() const
+	{
+	return iInternalServerFlags.IsSet(EInternalServerFlagsNotifyPending) && HasOwningSession();
+	}
+
+
+//*************************************************************************************
+/**
+ * Map the specified time object (data member) to the nearest minute (rounding down)
+ */
+void TASSrvAlarm::RoundDownTimeToMinute(TTimeType aType)
+	{
+	switch(aType)
+		{
+	case ETimeTypeNextDue:
+		ASSrvStaticUtils::RoundTimeDownToTheMinute(iNextDueTime);
+		break;
+	case ETimeTypeOriginalExpiry:
+		ASSrvStaticUtils::RoundTimeDownToTheMinute(iOriginalExpiryTime);
+		break;
+		}
+	}
+
+
+//*************************************************************************************
+/**
+ * Destroy this alarm. Removes it from the queue and marks it as inactive.
+ */
+void TASSrvAlarm::DeQueue()
+	{
+	ServerData().Queue().DeQueueAlarm(*this);
+	}
+
+
+//*************************************************************************************
+/**
+ * Reset this alarm back to a completely uninitialized state.
+ */
+void TASSrvAlarm::Reset()
+	{
+	TASShdAlarm::Reset();
+	//
+	iInternalServerFlags = 0;
+	iOriginatingSessionId = 0;
+	iSoundPeriodCycleNumber = 0;
+	}
+
+
+/**
+Clears the alarm's flags.
+*/
+void TASSrvAlarm::ClearFlags()
+	{
+	iFlags = 0;
+	}
+
+
+/**
+Set the alarm's sound state.
+
+@param aSoundState The new TSoundState of this alarm.
+*/
+void TASSrvAlarm::SetSoundState(TSoundState aSoundState)
+	{
+	iSoundState = aSoundState;
+	}
+
+
+//*************************************************************************************
+/**
+ * Toggle the sound state of this alarm
+ */
+void TASSrvAlarm::ToggleSoundState()
+	{
+	// Invert the existing sound state
+	if	(iSoundState == ESoundStatePlayingNothing)
+		iSoundState = ESoundStatePlayingSound;
+	else
+		iSoundState = ESoundStatePlayingNothing;
+	}
+
+
+//*************************************************************************************
+/**
+ * If sound is temporarily silenced, then this method is used to re-initialize
+ * the alarm's starting time for any future sound calculations.
+ */
+void TASSrvAlarm::ReinitializeSoundState(const TTime& aBaselineForSoundTiming)
+	{
+	// Reset the cycle and sound state ready to start playing
+	ASSrvStaticUtils::TodayAtTheSpecifiedTime(aBaselineForSoundTiming, iStartTimeForSoundCalculations);
+	SetSoundState(TASSrvAlarm::ESoundStatePlayingNothing);
+	SetSoundTimingCycleIndex(0);
+	}
+
+
+//*************************************************************************************
+/**
+ * Work out when the alarm sound should next start or stop playing.
+ */
+TTime TASSrvAlarm::CalculateAndPrepareNextSoundCycle()
+	{
+	// If the new state is not to play any sound, and we've already progressed
+	// through all the Sound Intervals, the SoundTimingCycleIndex will have
+	// been set to KErrNotFound (see below)
+	if ((SoundState() == ESoundStatePlayingNothing) &&
+		(SoundTimingCycleIndex() == KErrNotFound)
+#ifdef SYMBIAN_ALARM_REPEAT_EXTENSIONS
+		|| Continuous()
+#endif
+		)
+		{
+		SetSoundTimingCycleIndex(0); // just to be safe, we'll reset this.
+		return Time::NullTTime();
+		}
+
+	const CASSrvSoundSettings& soundSettings = ServerData().SoundSettings();
+
+	// If the new state is not to play any sound, then we should work out when 
+	// we should start playing sound again. If we're now about to start playing sound
+	// we should work out when we're going to stop playing it again.
+	CASSrvSoundSettings::TSoundCyclePosition position = CASSrvSoundSettings::ESoundCyclePositionAtStart;
+	if (SoundState() == ESoundStatePlayingSound)
+		{
+		position = CASSrvSoundSettings::ESoundCyclePositionAtEnd;
+		}
+
+	//
+#ifdef __DEBUGGING_TIMES
+	TDateTime startTimeForSoundCalculations = iStartTimeForSoundCalculations.DateTime();
+#endif
+
+	ASSrvStaticUtils::TodayAtTheSpecifiedTime(iStartTimeForSoundCalculations, iStartTimeForSoundCalculations);
+#ifdef __DEBUGGING_TIMES
+	startTimeForSoundCalculations = iStartTimeForSoundCalculations.DateTime();
+#endif
+
+	TTime returnTime(soundSettings.SoundIntervalTime(iStartTimeForSoundCalculations, SoundTimingCycleIndex(), position));
+#ifdef __DEBUGGING_TIMES
+	TDateTime returnTimeDT = returnTime.DateTime();
+#endif
+
+	// Increment position at the end of the cycle
+	if (SoundState() == ESoundStatePlayingSound)
+		{
+		// There will always be at least one sound play interval, so we can 
+		// assume that here. If there hadn't been any intervals, the CASSrvSoundController
+		// would be considered disabled, and wouldn't have invoked the current method.
+		TInt tempSS = SoundTimingCycleIndex();
+		if (tempSS >= (soundSettings.SoundIntervalCount()-1)) // Index E (0..SoundIntervalCount()-1)
+			{
+			switch ( soundSettings.RepeatSetting() )
+				{
+			case EAlarmSoundRepeatSettingLoop:
+				// Go back to the first cycle, resetting the start time
+				// to avoid setting an alarm time in the past.
+				tempSS = 0;
+				iStartTimeForSoundCalculations = returnTime;
+				break;
+			case EAlarmSoundRepeatSettingRepeatLast:
+				{
+				// Repeat the last interval; Don't change the Sound Interval
+				// Index (currently tempSS). The last offset will be added to
+				// iStartTimeForSoundCalculations in the call to SoundIntervalTime,
+				// so we need to update it so that the timing is correct.
+
+				// Determine the duration of the last interval:
+				TTime lastStartTime(soundSettings.SoundIntervalTime(iStartTimeForSoundCalculations,
+																	soundSettings.SoundIntervalCount()-1,
+																	CASSrvSoundSettings::ESoundCyclePositionAtStart ));
+				TTime nextToLastStartTime(soundSettings.SoundIntervalTime(iStartTimeForSoundCalculations,
+																	soundSettings.SoundIntervalCount()-2,
+																	CASSrvSoundSettings::ESoundCyclePositionAtStart ));
+				TTimeIntervalMinutes diff;
+				lastStartTime.MinutesFrom(nextToLastStartTime, diff);
+				iStartTimeForSoundCalculations += diff;
+				}
+				break;
+			case EAlarmSoundRepeatSettingStop:
+				// This will cause TTime::NullTTime() to be returned to the
+				// Sound Controller when the next time interval expires.
+				tempSS = KErrNotFound;
+				break;
+				}
+			}
+		else
+			{
+			tempSS++;
+			}
+		SetSoundTimingCycleIndex(tempSS);
+		}
+	return returnTime;
+	}
+
+
+//*************************************************************************************
+/**
+ * Perform some sanity checking after internalizing an alarm or receiving
+ * an alarm via the client API. Checks that the alarm isn't too old to 
+ * still be considered valid. If the alarm is okay, its status is changed
+ * to enabled. Note that this method checks the workdays, and hence it
+ * might also disable an otherwise "valid" alarm.
+ * 
+ * @return  KErrNone if the alarm is valid (and therefore may be queued),
+ *			KErrArgument if this alarm isn't valid, KErrLocked if a "Workdays" alarm
+ *			has no valid workdays to test itself against (but is otherwise valid).
+ */
+TInt TASSrvAlarm::ValidateAndEnable(TTimeIntervalSeconds aAllowableWindow, TBool aAllowAnyOnceAlarmInThePast, TBool aEnable)
+	{
+	TInt alarmErrorCode = KErrArgument;
+	//
+	if (OriginalExpiryTime() == Time::NullTTime())
+		{
+		OriginalExpiryTime() = NextDueTime();
+		}
+	// Was the alarm notifying or snoozed when the server closed?
+	// If so, we don't want to adjust the next due time.
+	if (State() == EAlarmStateNotifying)
+		{
+		// Set the state back to EAlarmStateQueued so the alarm will notify again.
+		iState = EAlarmStateQueued;
+		alarmErrorCode = KErrNone;
+		}
+	else if (State() == EAlarmStateSnoozed)
+		{
+		alarmErrorCode = KErrNone;
+		}
+	else
+		{
+#ifdef SYMBIAN_SYSTEM_STATE_MANAGEMENT
+		if (!(IsWakeup() && iCharacteristics.IsSet(EAlarmCharacteristicsSessionSpecific)))
+			{
+#endif
+			TTime oldestValidTimeForAlarms(ASSrvStaticUtils::UtcTimeNow());
+			if	(NextDueTime() != Time::NullTTime())
+				{
+				if	(RepeatDefinition() == EAlarmRepeatDefintionRepeatOnce)
+					{
+					// Does the alarm fall within the window?
+					oldestValidTimeForAlarms -= aAllowableWindow;
+			
+					// 1. Ignore alarms in the past (taking the window into account)
+					// 2. If the "allow anything" flag is set (parameter) then we always
+					//    allow the alarm.
+					// 3. If the alarm is in the past, but it's state is "Notified" then we 
+					//    allow it anyway.
+					const TBool insideWindow = NextDueTime() >= oldestValidTimeForAlarms;
+					if	(insideWindow || aAllowableWindow.Int() < 0 || aAllowAnyOnceAlarmInThePast || State() == EAlarmStateNotified)
+						{
+						// This alarm is okay
+						alarmErrorCode = KErrNone;
+						}
+					}
+				else
+					{
+					// The alarm must be one of the repeating types.
+					__ASSERT_ALWAYS(RepeatDefinition() != EAlarmRepeatDefintionRepeatOnce, ASSrvStaticUtils::Panic(ASSrvStaticUtils::EASSrvPanicInvalidAlarmRepeat));
+	
+					// If it's a repeat in the next 24 hours then we need to
+					// work out the next time. Otherwise, we can use the standard
+					// PrepareForNextRepeat method.
+					if	(RepeatDefinition() == EAlarmRepeatDefintionRepeatNext24Hours)
+						{
+						ASSrvStaticUtils::CalculateNext24HoursRepeat(*this, aAllowableWindow);
+						alarmErrorCode = KErrNone;
+						}
+					else
+					    {
+					    alarmErrorCode = PrepareForNextRepeat(aAllowableWindow);
+					    }
+					}
+#ifdef SYMBIAN_SYSTEM_STATE_MANAGEMENT
+				}
+#endif
+			}
+		}
+
+	// If the alarm is valid then we enable it. Note that this will leave (otherwise) valid
+	// "Workdays" alarms disabled (e.g. if there aren't any workdays defined by the user, then
+	// the alarm is valid, but we can't enable it because we don't know when it will next be
+	// due).
+	if	(alarmErrorCode == KErrNone && aEnable)
+		{
+		// Enable the alarm if its not rejected
+		const TInt error = doSetStatus(EAlarmStatusEnabled, EFalse);
+		__ASSERT_ALWAYS(error == KErrNone, ASSrvStaticUtils::Panic(ASSrvStaticUtils::EASSrvPanicCannotSetAlarmStatus)); // Something has really gone wrong...
+		}
+
+	return alarmErrorCode;
+	}
+
+
+//*************************************************************************************
+/**
+ * Snooze this alarm until the specified time
+ */
+void TASSrvAlarm::Snooze(const TTime& aTimeToAwaken)
+	{
+	TTime tempTTime = aTimeToAwaken;
+	ASSrvStaticUtils::RoundTimeDownToTheMinute(tempTTime);
+	NextDueTime() = tempTTime;
+
+	// Changing the state to snoozed will notify the alarm queue of a state change.
+	// At this point, the queue is resorted to ensure that the snoozed alarm is
+	// present in the queue at the right point (since a snoozed alarm may end up
+	// moving to the head of the queue).
+	SetState(EAlarmStateSnoozed);
+	}
+
+
+/**
+Returns ETrue if the alarm sound playing has been paused.
+
+@return ETrue if the alarm sound playing has been paused, EFalse otherwise.
+*/
+TBool TASSrvAlarm::HasSoundPaused() const
+	{
+	return iInternalServerFlags.IsSet(EASShdAlarmFlagsSoundHasBeenPaused);
+	}
+
+
+/**
+Sets an internal flag.
+
+@internalComponent
+*/
+void TASSrvAlarm::SetSoundPausedFlag()
+	{
+	iInternalServerFlags.Set(EASShdAlarmFlagsSoundHasBeenPaused);
+	}
+
+
+/**
+Clears an internal flag.
+
+@internalComponent
+*/
+void TASSrvAlarm::ClearSoundPausedFlag()
+	{
+	iInternalServerFlags.Clear(EASShdAlarmFlagsSoundHasBeenPaused);
+	}
+
+
+//
+//
+//
+
+
+//*************************************************************************************
+/**
+ * Set the data for this alarm. Leaves with KErrInUse if this alarm
+ * already has associated data.
+ */
+void TASSrvAlarm::DataAttachL(HBufC8* aData)
+	{
+	CASSrvDataPool& dataPool = ServerData().DataPool();
+	dataPool.DataPoolAddDataL(Id(), aData);
+
+	// Set the base-class flags
+	iFlags.Set(EASShdAlarmFlagsHasAssociatedData);
+
+	// make sure all data gets saved
+	ServerData().Queue().HandleAlarmDataChanged(Id());
+	}
+
+
+//*************************************************************************************
+/**
+ * Remove the data from the pool. Leaves if the alarm doesn't have
+ * any associated data.
+ */
+void TASSrvAlarm::DataDetachL()
+	{
+	CASSrvDataPool& dataPool = ServerData().DataPool();
+	dataPool.DataPoolRemoveDataL(Id());
+
+	// Clear the base-class flags
+	iFlags.Clear(EASShdAlarmFlagsHasAssociatedData);
+	}
+
+
+//*************************************************************************************
+/**
+ * Returns the size in bytes of the data associated with this alarm.
+ * Leaves with KErrNotFound if there is no data for this alarm.
+ */
+TInt TASSrvAlarm::DataSizeL() const
+	{
+	TInt size = 0;
+	CASSrvDataPool& dataPool = ServerData().DataPool();
+	//
+	if	(HasAssociatedData() && dataPool.DataPoolContainsEntry(Id()))
+		size = dataPool.DataPoolEntry(Id()).Size();
+	else
+		User::Leave(KErrNotFound);
+	//
+	return size;
+	}
+
+
+//*************************************************************************************
+/**
+ * Access the data associated with this alarm. Will leave with 
+ * KErrNotFound if this alarm doesn't have any data.
+ */
+const TDesC8& TASSrvAlarm::DataL() const
+	{
+	CASSrvDataPool& dataPool = ServerData().DataPool();
+	//
+	if	(!HasAssociatedData() || !dataPool.DataPoolContainsEntry(Id()))
+		User::Leave(KErrNotFound);
+	//
+	const TDesC8& data = dataPool.DataPoolEntry(Id());
+	return data;
+	}
+
+
+//*************************************************************************************
+/**
+ * Same as DataL() except this panics if the alarm doesn't have any
+ * data.
+ */
+const TDesC8& TASSrvAlarm::Data() const
+	{
+	return ServerData().DataPool().DataPoolEntry(Id());
+	}
+
+
+//
+//
+//
+
+
+//*************************************************************************************
+/**
+ * When attempting to action this alarm, a timer error occurred. Depending
+ * on the alarm type, a different action is performed.
+ */
+void TASSrvAlarm::HandleTimerError(TInt aErrorCode)
+	{
+	__ASSERT_ALWAYS(aErrorCode != KErrNone, ASSrvStaticUtils::Fault(ASSrvStaticUtils::EASSrvFaultPhantomErrorReported));
+
+	// Need to notify session if there was a pending notification request
+	NotificationMessageComplete(aErrorCode);
+
+	// Destroy the alarm
+	DeQueue();
+	}
+
+
+//*************************************************************************************
+/**
+ * Called when an alarm is removed from the queue. Notifies any observers
+ * that this alarm has died.
+ */
+void TASSrvAlarm::HandleDeQueue()
+	{
+	NotificationMessageComplete(KErrDied);
+	}
+
+
+/**
+Called when the date/time or work days changes.
+*/
+TBool TASSrvAlarm::HandleDateTimeChangedL(TUint aWorkdays, TBool aWorkdaysChanged)
+	{
+	// If this alarm has an outstanding expiry notification request it will be
+	// completed with KErrCancel.
+	NotificationMessageComplete(KErrCancel);
+	
+	TBool agendaAlarmInPast = EFalse;
+	TTime alarmDue;
+
+	// If the work days have changed (e.g. by a change in locale) and this alarm
+	// is a "work day" alarm then the alarm state is updated based upon the new
+	// work days.
+	if (aWorkdaysChanged && RepeatDefinition() == EAlarmRepeatDefintionRepeatWorkday)
+		{
+		if (!aWorkdays)
+			{
+			User::LeaveIfError(doSetStatus(EAlarmStatusDisabled, ETrue));
+			return agendaAlarmInPast;
+			}
+		// Enable the alarm now we have some work days.
+		const TInt error = doSetStatus(EAlarmStatusEnabled, ETrue);
+		__ASSERT_ALWAYS(error == KErrNone, ASSrvStaticUtils::Panic(ASSrvStaticUtils::EASSrvPanicCannotSetAlarmStatus));
+		PrepareForNextRepeat();
+		return agendaAlarmInPast;
+		}
+	
+	TTime timeNow = ASSrvStaticUtils::UtcTimeNow();
+	// tempTime is used for rounding purposes.  In most cases it is equal to
+	// NextDueTime().	
+	TTime tempTime(NextDueTime());
+	if (timeNow.DaysFrom(NextDueTime()).Int() == 0)
+		{
+		TTimeIntervalMinutes MinuteInterval;
+		timeNow.MinutesFrom(NextDueTime(), MinuteInterval);
+		tempTime = NextDueTime();
+		tempTime -= MinuteInterval; 
+		}
+
+	const TInt days(timeNow.DaysFrom(tempTime).Int() > 0 ?
+		timeNow.DaysFrom(tempTime).Int() :
+		timeNow.DaysFrom(NextDueTime()).Int());
+
+	// If the time has changed to the past we do not re-expire if the time
+	// crossed a previous boundary for this alarm.  If the time has changed
+	// to the future and crossed a repeat boundary then we expire
+	// immediately.
+	
+	switch(RepeatDefinition())
+		{
+	case EAlarmRepeatDefintionRepeatNext24Hours:
+		// Is next due time more than 1 day in the future?
+		if (days <= -1)
+			{
+			// Alarm changes to "repeat once" and at a fixed time.
+			TAlarmCharacteristicsFlags tempAFlags = Characteristics();
+			tempAFlags.Clear(EAlarmRepeatDefintionRepeatNext24Hours);
+			tempAFlags.Set(EAlarmRepeatDefintionRepeatOnce);
+			SetCharacteristicsL(tempAFlags, OriginatingSessionId());
+			RepeatDefinition() = EAlarmRepeatDefintionRepeatOnce;
+			}
+		// Fall through intentionally so that the following case is applied if
+		// the alarm repeat definition has been changed above from "next 24
+		// hours" to "repeat once". 
+
+	case EAlarmRepeatDefintionRepeatOnce:
+		timeNow.DateTime().SetSecond(0);
+		alarmDue = NextDueTime();
+		if (!iCharacteristics.IsSet(EAlarmCharacteristicsIsFixed))
+	        {
+			// We need to compare alarm due time and current time in local.
+			// Since it is a floating alarm the offset for the alarm due time is
+	        // taken from previous offset stored with server. 
+		    alarmDue += ServerData().CachedUtcOffset();
+		    timeNow += User::UTCOffset();	        
+	        }
+		if (days >= 1 || (iCharacteristics.IsSet(EAlarmCharacteristicsDeQueueIfDueTimeInPast) && alarmDue < timeNow))
+			{
+			// The new time is at least one day after the original expiry time.
+			// Alternatively it is an agenda alarm which does not want to be
+			// notified if it is in the past.  We delete the alarm silently.
+			if (iCharacteristics.IsSet(EAlarmCharacteristicsDeQueueIfDueTimeInPast) && State() != EAlarmStateNotified)
+				{
+				agendaAlarmInPast = ETrue;
+				}
+			DeQueue();
+			}
+		break;
+
+	case EAlarmRepeatDefintionRepeatDaily:
+	case EAlarmRepeatDefintionRepeatWorkday:
+	case EAlarmRepeatDefintionRepeatWeekly:
+#ifdef SYMBIAN_ALARM_REPEAT_EXTENSIONS
+	case EAlarmRepeatDefinitionRepeatDailyOnGivenDays:
+#endif
+		// When days is 0 the alarm would be within 24 hours either in the
+		// future or in the past in which case there is no need to recalculate
+		// the next due time.
+		if (days != 0)
+			{
+			PrepareForNextRepeat();
+			}
+		break;
+
+	default:
+		__ASSERT_DEBUG(EFalse, ASSrvStaticUtils::Panic(ASSrvStaticUtils::EASSrvPanicInvalidAlarmRepeat));
+		break;
+		}
+		
+	return agendaAlarmInPast;
+	}
+
+
+//
+//
+//
+
+
+//*************************************************************************************
+/**
+ * @see MASSrvAlarmTimerObserver
+ */
+void TASSrvAlarm::MATimerHandleAlarmExpired(TAlarmTimerEvent aEvent, TAlarmId aAlarmId)
+	{
+	// We're only interested in alarm expiry events relating to this alarm.
+	if	(aAlarmId != Id())
+		return;
+
+	if	(iInternalServerFlags.IsSet(EInternalServerFlagsNotifyPending))
+		{
+		// Map the event onto an error value
+		TInt completionCode = KErrNone;
+		switch(aEvent)
+			{
+		case EAlarmTimerEventAlarmExpired:
+			completionCode = KErrNone;
+			break;
+		case EAlarmTimerEventTimeOrDateChanged:
+			completionCode = KErrAbort;
+			break;
+		case EAlarmTimerEventTimingError:
+			completionCode = KErrGeneral;
+			break;
+		default:
+			__ASSERT_DEBUG(EFalse, ASSrvStaticUtils::Fault(ASSrvStaticUtils::EASSrvFaultTimerEventNotHandled));
+			break;
+			}
+
+		// Now complete the message
+		NotificationMessageComplete(completionCode);
+		}
+	}
+
+/**
+Return the server data object.
+
+@return The server data object created by the server that owns all the data.
+*/
+CASSrvServerWideData& TASSrvAlarm::ServerData() const
+	{
+	return iServerWideData;
+	}
+
+
+//
+//
+//
+
+
+//*************************************************************************************
+/**
+ * Complete an outstanding notification request
+ */
+void TASSrvAlarm::NotificationMessageComplete(TInt aCompletionCode)
+	{
+	if	(iInternalServerFlags.IsSet(EInternalServerFlagsNotifyPending))
+		{
+		// Complete the outstanding request
+		iNotificationMessage.Complete(aCompletionCode);
+
+		// Update flags to indicate we no longer have a pending 
+		// notification message pointer
+		iInternalServerFlags.Clear(EInternalServerFlagsNotifyPending);
+
+		// Reset session identified
+		SetOriginatingSessionId(KErrNotFound);
+
+		// Done with this now
+		ServerData().Timer().NotifyAlarmExpiredCancel(*this);
+		}
+	}
+
+
+/**
+Calculate when this alarm is next due to expire.
+
+@param aAllowableWindow
+	This is a delta to apply to the curren time when calculating the next expiry
+	time.  The delta is usually 0 seconds, however, when alarms are internalized
+	from the backup store, we apply a 59 second delta which allows alarms which
+	are less than one minute old to be treated as "not yet expired".  This
+	allows devices with unpredictable start up times to show alarm expiry
+	dialogs rather than alams silently going missing.
+
+@return
+	KErrNone if the alarm state is consistent, KErrArgument if this method is
+	being called for the wrong type of alarm or KErrLocked if a "work day"
+	alarm has no valid work days to test itself against.
+*/
+TInt TASSrvAlarm::PrepareForNextRepeat(TTimeIntervalSeconds aAllowableWindow)
+	{
+	// This should never be called for "repeat once" or "repeat next 24 hour"
+	// alarms.
+	__ASSERT_DEBUG
+		(
+		RepeatDefinition() != EAlarmRepeatDefintionRepeatOnce &&
+		RepeatDefinition() != EAlarmRepeatDefintionRepeatNext24Hours,
+		ASSrvStaticUtils::Panic(ASSrvStaticUtils::EASSrvPanicInvalidAlarmRepeat)
+		);
+	if (RepeatDefinition() == EAlarmRepeatDefintionRepeatOnce ||
+		 RepeatDefinition() == EAlarmRepeatDefintionRepeatNext24Hours
+		)
+		{
+		return KErrArgument;
+		}
+
+	// This alarm has just been acknowledged. If it repeats we need to
+	// reschedule it.
+	TTime timeNow(ASSrvStaticUtils::UtcTimeNow());
+
+	// CTimer::At() can complete >1 second early.	
+	timeNow += TTimeIntervalSeconds(KAlarmServerCTimerFudgeTimeInSeconds);
+
+	TTimeIntervalDays daysToAddFromNow = 0;
+	TTimeIntervalDays rollOverDaysToAddFromNow = 7;
+
+	// Update the nextRepeat object to contain the right hour and minutes
+	// component from the last repeat time.
+	TTime nextRepeat;
+	
+	TDateTime nextRepeatDateTime = timeNow.DateTime();
+	const TDateTime oldTime = OriginalExpiryTime().DateTime();
+	nextRepeatDateTime.SetHour(oldTime.Hour());
+	nextRepeatDateTime.SetMinute(oldTime.Minute());
+	nextRepeat = nextRepeatDateTime;
+	
+	// Use local time to find the current day number in the week. 
+	TTime nextRepeatLocal = nextRepeat + User::UTCOffset();
+
+	// Remove seconds and microseconds part.
+	ASSrvStaticUtils::RoundTimeDownToTheMinute(nextRepeat);
+	
+	switch (RepeatDefinition())
+		{
+	case EAlarmRepeatDefintionRepeatDaily:
+		{
+		// The time is fixed, but the day changes.
+		rollOverDaysToAddFromNow = 1;
+		}
+		break;
+
+	case EAlarmRepeatDefintionRepeatWeekly:
+		{
+		// Same day, next week. 
+		daysToAddFromNow = OriginalExpiryTime().DayNoInWeek() - timeNow.DayNoInWeek();
+		}
+		break;
+	
+	case EAlarmRepeatDefintionRepeatWorkday:
+		{
+		// Same time at next work day.
+		const TUint KWorkDays = TLocale().WorkDays();
+		if (!KWorkDays)
+			{
+			// If there are no work days defined then we disable the alarm.
+			// When a change in work days is detected (caused by a change in
+			// locale) the HandleDateTimeChanged() method will be called and the
+			// alarm will be enabled again.
+#ifdef _DEBUG
+			const TInt ret = 
+#endif
+			doSetStatus(EAlarmStatusDisabled, ETrue);
+			__ASSERT_DEBUG(ret == KErrNone, ASSrvStaticUtils::Panic(ASSrvStaticUtils::EASSrvPanicCannotSetAlarmStatus));
+			return KErrLocked;
+			}
+        ASSrvStaticUtils::DaysUntilNextActiveAlarmDay(daysToAddFromNow,
+        	rollOverDaysToAddFromNow, nextRepeatLocal.DayNoInWeek(), KWorkDays);
+		}
+		break;
+	
+#ifdef SYMBIAN_ALARM_REPEAT_EXTENSIONS
+	case EAlarmRepeatDefinitionRepeatDailyOnGivenDays:
+		{
+		// Same time at next alarm day.
+        ASSrvStaticUtils::DaysUntilNextActiveAlarmDay(daysToAddFromNow,
+        	rollOverDaysToAddFromNow, nextRepeatLocal.DayNoInWeek(),
+        	AlarmDays());
+		}
+		break;
+#endif
+
+	default:
+		{
+		__ASSERT_DEBUG(EFalse, ASSrvStaticUtils::Panic(ASSrvStaticUtils::EASSrvPanicInvalidAlarmRepeat));
+		}
+		break;
+		}
+
+	// Add the number of days on so that we repeat on the right day in the
+	// future.
+	nextRepeat += daysToAddFromNow;
+
+    // If the NextDueTime() is after the nextRepeat time that has been
+	// calculated so far then the alarm has already notified and been
+	// rescheduled for its next repeat.  We don't enable the allowable window in
+	// this case because it is possible that the alarm could be rescheduled for
+	// the last repeat (that it has already notified about) if it is within the
+	// allowable window.
+	if (NextDueTime() <= nextRepeat)
+		{
+		timeNow -= aAllowableWindow;
+		}
+
+	// If the calculated time is still before the current time then add the
+	// roll-over period too.  This mirrors the EALWL repeat code.  We also allow
+	// a window (specified as a parameter to this function in seconds) so that
+	// during startup we don't expire alarms which are less than a minute old.
+	if (nextRepeat < timeNow)
+	    {
+	    nextRepeat += rollOverDaysToAddFromNow;
+        // If calculated time after adding one rollOverDay is still before the
+	    // current time then move it one day forward.
+        if (RepeatDefinition() == EAlarmRepeatDefintionRepeatWorkday && 
+        	nextRepeat - TTimeIntervalDays(rollOverDaysToAddFromNow.Int() - 1) < timeNow)
+            {
+            nextRepeat += TTimeIntervalDays(1);
+            }
+	    }
+
+	// Update next due time and original expiry time.
+	NextDueTime() = nextRepeat;
+	OriginalExpiryTime() = nextRepeat;
+
+	return KErrNone;
+	}
+
+
+/**
+Sets the alarm's sound timing cycle index number.
+
+@param aSoundNumber The alarm's sound timing cycle index number.
+*/
+void TASSrvAlarm::SetSoundTimingCycleIndex(TASSrvAlarmSoundCycleNumber aSoundNumber)
+	{
+	iSoundPeriodCycleNumber = aSoundNumber;
+	}
+
+
+/**
+Gets the alarm's sound timing cycle index number.
+
+@return The alarm's sound timing cycle index number.
+*/
+TASSrvAlarmSoundCycleNumber TASSrvAlarm::SoundTimingCycleIndex() const
+	{
+	return iSoundPeriodCycleNumber;
+	}
+
+
+/**
+ * Internalize of the Alarm Queue after a Restore (from a backup)
+ * deletes the old queue.  This method sends a Cancel notification for
+ * a 'Session Alarm' to its the TRequestStatus object.
+ */
+void TASSrvAlarm::CancelSessionAlarm()
+	{
+	// If there was a notification request then we complete it
+	if(iCharacteristics.IsSet(EAlarmCharacteristicsSessionSpecific))
+		NotificationMessageComplete(KErrCancel);
+	}
+	
+/**
+Set alarm status and update disable flag.
+
+@param aStatus
+	Alarm status to set.
+	
+@param aAutoDisabled
+	If it's called by locale change handler.
+*/
+TInt TASSrvAlarm::doSetStatus(TAlarmStatus aStatus, TBool aAutoDisabled)
+	{
+	const TAlarmStatus oldStatus = Status();
+	
+	if (oldStatus == aStatus)
+		{
+		if (aStatus == EAlarmStatusDisabled && !aAutoDisabled) 
+			{
+			//Change from auto-disabled to manual-disabled
+			iFlags.Set(EASShdAlarmFlagsPermanentDisabled);	
+			}
+		return KErrNone;	
+		}
+	
+	switch (aStatus)
+		{
+	case EAlarmStatusEnabled:
+		if (RepeatDefinition() == EAlarmRepeatDefintionRepeatWorkday)
+			{
+			if (aAutoDisabled && iFlags.IsSet(EASShdAlarmFlagsPermanentDisabled))
+				{
+				// Called by locale change handler and we will not enable the
+				// alarm as it has been disabled manually.
+				return KErrNone;
+				}
+				
+			// Calculate its next valid expiry date.  Will return KErrLocked if
+			// there are no work days defined.
+			const TInt error = PrepareForNextRepeat();
+			if (error != KErrNone)
+				{
+				return error;
+				}
+			}
+		iFlags.Clear(EASShdAlarmFlagsPermanentDisabled);	
+		break;
+
+	case EAlarmStatusDisabled:
+		if(aAutoDisabled)
+			{
+			iFlags.Clear(EASShdAlarmFlagsPermanentDisabled);	
+			}
+		else
+			{
+			iFlags.Set(EASShdAlarmFlagsPermanentDisabled);	
+			}	
+		break;
+
+	default:
+		__ASSERT_DEBUG(EFalse, ASSrvStaticUtils::Fault(ASSrvStaticUtils::EASSrvFaultAlarmStatusNotHandled));
+		break;
+		}
+
+	// Update status now.
+	iStatus = aStatus;
+
+	// Notify queue so that it can work out if it needs to do
+	// some re-ordering (only do this if we've been queued, and therefore
+	// have an alarm id).
+	if (Id() != KNullAlarmId)
+		{
+		ServerData().Queue().HandleAlarmStatusChanged(Id(), oldStatus);
+		}
+
+	return KErrNone;
+	}
+