commonappservices/alarmserver/Server/Source/ASSrvAlarmQueue.cpp
changeset 0 2e3d3ce01487
child 19 924385140d98
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/commonappservices/alarmserver/Server/Source/ASSrvAlarmQueue.cpp	Tue Feb 02 10:12:00 2010 +0200
@@ -0,0 +1,1604 @@
+// 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: Implementation of the class representing the Alarm Queue 
+//
+
+#include <e32property.h>
+#include "ASSrvAlarmQueue.h"
+
+
+// User Includes
+#include "ASSrvTimer.h"
+#include "ASSrvStaticUtils.h"
+#include "ASSrvServerWideData.h"
+#include "ASSrvAnyEventManager.h"
+#include "ASSrvIteratorByState.h"
+#include "ASSrvIteratorByStatus.h"
+#ifdef SYMBIAN_SKIPPED_CALENDAR_ALARMS
+#include "ASSrvIteratorByCategory.h"
+#endif
+#ifdef SYMBIAN_SYSTEM_STATE_MANAGEMENT
+#include "ASSrvIteratorByWakeup.h"
+#endif
+#include "ASSrvEnvironmentChangeManager.h"
+#include "ASSrvSoundSettings.h"
+#include "ASSrvDataPool.h"
+#include "ASSrvDSTChange.h"
+
+
+// Constants
+const TInt KASSrvNumberOfDaysInOneWeek = 7;
+
+// Macro for security policy objects
+static _LIT_SECURITY_POLICY_PASS(KReadPolicy);
+static _LIT_SECURITY_POLICY_S0(KWritePolicy, 0x101f5027);
+
+// Enumerations
+#define DEBUG_PRINT_QUEUE(a){   RDebug::Print(_L(a));                  \
+                                 for(TInt i=0; i<iAlarms.Count(); i++)  \
+                                     {                                  \
+                                     const TASSrvAlarm& alarm = QueueAlarmAt(i); \
+                                     RDebug::Print(_L("ALARMSERVER\t Alarm[%02d] \"%S\" (%d)"), i, &alarm.Message(), alarm.Id()); \
+                                     }                              \
+                                RDebug::Print(_L("\n"));            \
+                            }
+
+// Uncomment the following define macro for getting logs in verbose mode
+// #define VERBOSE_DEBUG
+
+#if defined(VERBOSE_DEBUG)
+#define DEBUG_PRINT1(A)			RDebug::Print(A)
+#define DEBUG_PRINT2(A,B)		RDebug::Print(A,B)
+#else
+#define DEBUG_PRINT1(A)
+#define DEBUG_PRINT2(A,B)
+#endif
+
+
+CASSrvAlarmQueue::CASSrvAlarmQueue(CASSrvServerWideData& aServerWideData)
+    :iServerWideData(aServerWideData)
+	{
+	}
+
+CASSrvAlarmQueue::~CASSrvAlarmQueue()
+	{
+	ServerData().EnvironmentChangeManager().RequestEnvironmentChangesCancel(*this);
+	iAlarmObservers.Close();
+ 	iAlarms.ResetAndDestroy();
+ 	iAlarms.Close();
+	if (iInternalizeAlarmQueue.Count() != 0)
+	    {
+	    iInternalizeAlarmQueue.ResetAndDestroy();
+	    }
+	iInternalizeAlarmQueue.Close();
+ 	iNotificationList.Close();
+ 	delete iASSrvDSTChange;
+
+#ifdef SYMBIAN_SYSTEM_STATE_MANAGEMENT
+ 	iSsmRtcAdaptation.Close();
+#endif
+	}
+
+void CASSrvAlarmQueue::ConstructL()
+	{
+	ServerData().EnvironmentChangeManager().RequestEnvironmentChangesL(*this);
+	iPreviousUtcOffset = User::UTCOffset();
+	iASSrvDSTChange = CASSrvDSTChange::NewL(*this);
+
+#ifdef SYMBIAN_SYSTEM_STATE_MANAGEMENT
+	iSsmRtcAdaptation.Connect();
+	// Notify the listeners of 'KWakeupAlarmPubSubKey' key that the alarm queue is yet  to be internalized 
+	// by Alarm Server on its start-up at the device boot time.
+	RProperty::Define(KAlarmServerPubSubCategory, KWakeupAlarmPubSubKey, RProperty::EInt, KReadPolicy, KWritePolicy);
+	RProperty::Set( KAlarmServerPubSubCategory, KWakeupAlarmPubSubKey, EActiveWakeupAlarmUninitialized );
+	DEBUG_PRINT1(_L("Set the KWakeupAlarmPubSubKey to EActiveWakeupAlarmUninitialized (startup)"));
+#endif
+	}
+
+CASSrvAlarmQueue* CASSrvAlarmQueue::NewL(CASSrvServerWideData& aServerWideData)
+	{
+	CASSrvAlarmQueue* self = new(ELeave) CASSrvAlarmQueue(aServerWideData);
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+/**
+ * @see MASSrvEnvironmentChangeObserver
+ */
+void CASSrvAlarmQueue::MEnvChangeHandleEvent(TInt aChanges, TUint aWorkdays, TBool aWorkdaysChanged)
+	{
+	// Locale changes can be triggered by a lot of reasons, process locale
+	// changes only due to utc offset change and workdays change.
+	if ( (aChanges & EChangesSystemTime) || aWorkdaysChanged || (aChanges & EChangesLocale && (iPreviousUtcOffset != User::UTCOffset())) )
+		{
+#ifdef SYMBIAN_SKIPPED_CALENDAR_ALARMS
+		TBool possibleHiddenFloatingAlarm(EFalse);
+		// Store the time of the first calendar alarm, as it may have been skipped		
+		TTime firstSkippedAlarmLocalTime(0);
+		
+		const TAlarmId headCalendarAlarmId = HeadCalendarAlarmId();
+		if (headCalendarAlarmId != KNullAlarmId)
+			{
+			const TASSrvAlarm* headCalendarAlarm = QueueAlarmById(headCalendarAlarmId);
+			firstSkippedAlarmLocalTime = headCalendarAlarm->NextDueTime();
+		
+			// The calendar server adds a single alarm at a time.  If this 
+			// alarm is fixed, a skipped alarm instances notification must be 
+			// sent, since the calendar server may have had other unqueued 
+			// alarms that were skipped.
+			possibleHiddenFloatingAlarm = !headCalendarAlarm->IsFloating() && (headCalendarAlarm->NextDueTime() + iPreviousUtcOffset < ASSrvStaticUtils::LocalTimeNow());
+			}
+		//Retrive the old local time before the time zone has been changed for the first skipped alarm.
+		firstSkippedAlarmLocalTime = firstSkippedAlarmLocalTime + ServerData().CachedUtcOffset();
+		TBool skippedAgendaAlarm(EFalse);
+		
+		// Tell every alarm that the date/time has changed
+		const TInt count = QueueAlarmCount();
+		
+		TRAP_IGNORE(
+                    for (TInt i = count - 1; i >= 0; i--)
+                        {
+                        TASSrvAlarm& alarm = QueueAlarmAt(i);
+                        if (alarm.HandleDateTimeChangedL(aWorkdays, aWorkdaysChanged))
+                            {
+                            skippedAgendaAlarm = ETrue;	
+                            }
+                        }
+                    )
+		    		
+		// If there was a skipped agenda alarm, publish the environment change and the details of the first
+		// skipped alarm, otherwise, if there was a UTC offset change, only publish the environment change
+		if (skippedAgendaAlarm)
+			{
+			PublishSkippedAlarm(ETrue);
+			PublishAlarmedInstanceParams(firstSkippedAlarmLocalTime, aChanges & EChangesSystemTime);
+			}
+		else if (possibleHiddenFloatingAlarm)
+			{
+			PublishAlarmedInstanceParams(firstSkippedAlarmLocalTime, aChanges & EChangesSystemTime);			
+			}
+		else if (iPreviousUtcOffset != User::UTCOffset())
+			{
+			PublishSkippedAlarm(EFalse);
+			}
+#else
+		// Tell every alarm that the date/time has changed
+		const TInt count = QueueAlarmCount();
+			
+		TBool skippedAgendaAlarm = EFalse;
+			TRAP_IGNORE(
+	    		for (TInt i = count - 1; i >= 0; i--)
+	    			{
+	    			TASSrvAlarm& alarm = QueueAlarmAt(i);
+	 				if (alarm.HandleDateTimeChangedL(aWorkdays, aWorkdaysChanged))
+	    				{
+	    				skippedAgendaAlarm = ETrue;	
+	    				}
+	   				}
+			    )
+		if (skippedAgendaAlarm || iPreviousUtcOffset != User::UTCOffset())
+			{
+			PublishSkippedAlarm(skippedAgendaAlarm);
+			}
+#endif		
+	
+		//Re sort the alarms after the above date/time change
+  		TLinearOrder<TASSrvAlarm> order(ASSrvStaticUtils::CompareAlarms);
+  		iAlarms.Sort(order);
+
+		// Update floating alarms' due times.
+		UpdateFloatingDueTimes();
+
+		// When the date/time/locale changes we always simulate a change in the head item so that the timer will reset itself.
+		const TAlarmId newHeadItemId = HeadAlarmId();
+		NotifyEvent(MASSrvAlarmQueueObserver::EASSrvAlarmQueueEventHeadItemChanged, newHeadItemId);
+
+		// Global any event notification
+		ServerData().AnyEventManager().MASAnyEventManagerObserverNotifyChanges(EAlarmChangeEventSystemDateTimeChanged, KNullAlarmId);
+
+		// Remove the dead alarms
+		RemoveDeadAlarms();
+		
+		// And update the UTC offset			
+		iPreviousUtcOffset = User::UTCOffset();
+		}
+
+	if	(aChanges & EChangesMidnightCrossover)
+		{
+		//Tidy up alarm queue every midnight
+		RemoveDeadAlarms(); 
+		}
+	}
+
+/**
+ * Request notification when the state or status of an alarm changes
+ */
+void CASSrvAlarmQueue::RequestAlarmObservationEventsL(MASSrvAlarmObserver& aObserver)
+	{
+	User::LeaveIfError(iAlarmObservers.Append(TASSrvAlarmObserverMapplet(aObserver)));
+	}
+
+/**
+ * Cancel a previous notification request
+ */
+void CASSrvAlarmQueue::RequestAlarmObservationEventsCancel(MASSrvAlarmObserver& aObserver)
+	{
+	const TInt count = iAlarmObservers.Count();
+	for(TInt i=0; i<count; i++)
+		{
+		TASSrvAlarmObserverMapplet& mapplet = iAlarmObservers[i];
+		if	(&mapplet.Observer() == &aObserver)
+			{
+			iAlarmObservers.Remove(i);
+			return;
+			}
+		}
+	}
+
+/**
+ * Temporarily enabled or disable notifications
+ */
+void CASSrvAlarmQueue::RequestAlarmObservationEventsEnabled(MASSrvAlarmObserver& aObserver, TBool aEnabled)
+	{
+	const TInt count = iAlarmObservers.Count();
+	for(TInt i=0; i<count; i++)
+		{
+		TASSrvAlarmObserverMapplet& mapplet = iAlarmObservers[i];
+		if	(&mapplet.Observer() == &aObserver)
+			{
+			mapplet.SetEnabled(aEnabled);
+			return;
+			}
+		}
+	ASSrvStaticUtils::Panic(ASSrvStaticUtils::EASSrvPanicNoMatchingObserver);
+	}
+
+/**
+ * Adds the specified alarm to the alarm queue. Updates the alarm object
+ * so that it contains a uniquely allocated id, suitable for clients to identify
+ * this alarm amongst all others.
+ */
+void CASSrvAlarmQueue::QueueAlarmAndAllocateIdL(TASSrvAlarm& aAlarm, TAlarmId aSpecificAlarmId)
+	{
+	// Get the head id so that we know if the queue has changed
+	const TAlarmId headItemId = HeadAlarmId();
+
+	// Now allocate an id for the alarm. Doesn't matter if adding
+	// the alarm fails (and therefore we orphan an Id - they can
+	// be reused, as long as no current alarm shares the same id as 
+	// another).
+	const TAlarmId newId = aSpecificAlarmId == KNullAlarmId? NextFreeAlarmId() : aSpecificAlarmId;
+	aAlarm.Id()=newId;
+	
+	// Add the alarm
+	TASSrvAlarm* alarm = new(ELeave) TASSrvAlarm(ServerData());
+	CleanupStack::PushL(alarm);
+	*alarm = aAlarm;
+	//
+	TLinearOrder<TASSrvAlarm> order(ASSrvStaticUtils::CompareAlarms);
+	User::LeaveIfError(iAlarms.InsertInOrderAllowRepeats(alarm, order));
+	CleanupStack::Pop(alarm);
+
+	// Change the state to queued
+	TASSrvAlarm* newAlarm = QueueAlarmById(newId);
+	newAlarm->SetState(EAlarmStateQueued);
+
+	// Notify we've added an alarm
+	NotifyEvent(MASSrvAlarmQueueObserver::EASSrvAlarmQueueEventAlarmAdded, newAlarm->Id());
+
+	// Did the head item change?
+	const TAlarmId newHeadItemId = HeadAlarmId();
+	if	(newHeadItemId != headItemId)
+		{
+		// Head item has changed. The timer will pick up the change and requeue itself
+		// for the new alarm.
+		NotifyEvent(MASSrvAlarmQueueObserver::EASSrvAlarmQueueEventHeadItemChanged, newHeadItemId);
+		}
+
+	ServerData().AnyEventManager().MASAnyEventManagerObserverNotifyChanges(EAlarmChangeEventAlarmAddition, newId);
+	}
+
+/**
+ * Releases a previously allocated alarm. Removes it from the queue, and if this
+ * alarm was at the head of the queue, a new alarm is promoted.
+ */
+void CASSrvAlarmQueue::DeQueueAlarm(const TASSrvAlarm& aAlarm)
+	{
+	const TAlarmId id = aAlarm.Id();
+	const TBool isHeadItem = (id == HeadAlarmId());
+
+	// Find index from alarm
+	TIdentityRelation<TASSrvAlarm> identityRelation(ASSrvStaticUtils::CompareAlarmsExact);
+	const TInt errorOrIndex = iAlarms.Find(&aAlarm, identityRelation);
+	__ASSERT_ALWAYS(errorOrIndex != KErrNotFound, ASSrvStaticUtils::Fault(ASSrvStaticUtils::EASSrvFaultAlarmNotFound));
+
+	// Inform alarm it's being destroyed
+	TASSrvAlarm* alarm = &QueueAlarmAt(errorOrIndex);
+	__ASSERT_ALWAYS(alarm->Id() == aAlarm.Id(), ASSrvStaticUtils::Panic(ASSrvStaticUtils::EASSrvPanicAttemptingToDequeWrongAlarm));
+	alarm->HandleDeQueue();
+
+	// DEBUG_PRINT_QUEUE("ALARMSERVER Before removing item")
+
+	// Remove from queue
+#ifdef _DEBUG
+	const TInt count = QueueAlarmCount();
+#endif
+	iAlarms.Remove(errorOrIndex);
+	delete alarm;
+	__ASSERT_DEBUG(iAlarms.Count() == count-1, ASSrvStaticUtils::Panic(ASSrvStaticUtils::EASSrvPanicAlarmNotDeQueued));
+
+	// DEBUG_PRINT_QUEUE("ALARMSERVER After removing item")
+
+	// Notify we've deleted an alarm
+	NotifyEvent(MASSrvAlarmQueueObserver::EASSrvAlarmQueueEventAlarmDeleted, id);
+
+	// DEBUG_PRINT_QUEUE("ALARMSERVER After notifying event")
+
+	// Notify change
+	ServerData().AnyEventManager().MASAnyEventManagerObserverNotifyChanges(EAlarmChangeEventAlarmDeletion, id);
+
+	// DEBUG_PRINT_QUEUE("ALARMSERVER After notifying any event")
+
+	if	(isHeadItem)
+		{
+		// Head item has changed. The timer will pick up the change and requeue itself
+		// for the new alarm.
+		const TAlarmId newHeadItem = HeadAlarmId();
+		NotifyEvent(MASSrvAlarmQueueObserver::EASSrvAlarmQueueEventHeadItemChanged, newHeadItem);
+
+		// DEBUG_PRINT_QUEUE("ALARMSERVER After head item changed")
+		}
+	}
+
+/**
+ * Returns the alarm at the head of the queue or NULL if there is none.
+ */
+const TASSrvAlarm* CASSrvAlarmQueue::HeadAlarm() const
+	{
+	// There is a head alarm if there is at least one alarm in the queue
+	// and that alarm is not disabled.
+	CASSrvAlarmQueue& self = const_cast<CASSrvAlarmQueue&>(*this);
+
+	// Create & open primary iterator
+	RASSrvIteratorByStatus primaryIterator(self, EAlarmStatusEnabled);
+	primaryIterator.Open();
+
+	// Create and attach secondary iterator
+	RASSrvIteratorByState secondaryIterator(self, EAlarmStateQueued, EAlarmStateSnoozed);
+	primaryIterator.IteratorAttach(secondaryIterator);
+
+	if (primaryIterator.NextAlarmAvailable())
+		{
+		return QueueAlarmById(primaryIterator.NextAlarm().Id());
+		}
+	else
+		{
+		return NULL;
+		}
+	}
+
+#ifdef SYMBIAN_SKIPPED_CALENDAR_ALARMS	
+/**
+ * Returns the calendar alarm at the head of the queue
+ */
+TAlarmId CASSrvAlarmQueue::HeadCalendarAlarmId() const
+	{
+	CASSrvAlarmQueue& self = const_cast<CASSrvAlarmQueue&>(*this);
+
+	// Create & open primary iterator
+	RASSrvIteratorByStatus primaryIterator(self, EAlarmStatusEnabled);
+	primaryIterator.Open();
+
+	// Create and attach secondary iterator
+	RASSrvIteratorByState secondaryIterator(self, EAlarmStateQueued, EAlarmStateSnoozed);
+	primaryIterator.IteratorAttach(secondaryIterator);
+	
+	// The category for calendar alarms is defined in caalarm.h and is
+	// reproduced here to avoid a dependency on the calendar server
+	const TUid KUidAgendaModelAlarmCategory = { 0x101F4A70 };
+	
+	// Create and attach tertiary iterator
+	RASSrvIteratorByCategory tertiaryIterator(self, KUidAgendaModelAlarmCategory);
+	primaryIterator.IteratorAttach(tertiaryIterator);
+
+	TAlarmId returnAlarmId(KNullAlarmId);
+	
+	if (primaryIterator.NextAlarmAvailable())
+		{
+		returnAlarmId = primaryIterator.NextAlarm().Id();
+		}
+	
+	return returnAlarmId;
+	}
+#endif
+
+#ifdef SYMBIAN_SYSTEM_STATE_MANAGEMENT
+/**
+ * Returns the alarm id of the head wakeup alarm of the queue
+ */
+TAlarmId CASSrvAlarmQueue::HeadWakeupAlarmId() const
+	{
+	// There is a head alarm if there is at least one alarm in the queue
+	// and that alarm is not disabled.
+	CASSrvAlarmQueue& self = const_cast<CASSrvAlarmQueue&>(*this);
+
+	// Create & open primary iterator
+	RASSrvIteratorByStatus primaryIterator(self, EAlarmStatusEnabled);
+	primaryIterator.Open();
+
+	// Create and attach secondary iterator
+	RASSrvIteratorByState secondaryIterator(self, EAlarmStateQueued, EAlarmStateSnoozed);
+	primaryIterator.IteratorAttach(secondaryIterator);
+	
+	// Create and attach tertiary iterator	
+	RASSrvIteratorByWakeup tertiaryIterator(self);
+	secondaryIterator.IteratorAttach(tertiaryIterator);
+	
+	TAlarmId returnAlarmId(KNullAlarmId);
+	
+	if (primaryIterator.NextAlarmAvailable())
+		{
+		returnAlarmId = primaryIterator.NextAlarm().Id();
+		}
+	
+	return returnAlarmId;
+	}
+#endif
+
+/**
+ * Returns the id of the alarm at the head of the queue, or if there isn't a head alarm returns KNullAlarmId
+ */
+TAlarmId CASSrvAlarmQueue::HeadAlarmId() const
+	{
+	TAlarmId headItemId = KNullAlarmId;
+	const TASSrvAlarm* headAlarm = HeadAlarm();
+	if	(headAlarm)
+		{
+		headItemId = headAlarm->Id();
+		}
+	return headItemId;
+	}
+
+/**
+ * Called whenever a session logs off. All session-specific alarm should be removed in this instance.
+ */
+void CASSrvAlarmQueue::RemoveAllSessionAlarmsBySessionId(TASSrvSessionId aSessionId)
+	{
+	const TInt count = QueueAlarmCount();
+	for(TInt i=count-1; i>=0; i--)
+		{
+		const TASSrvAlarm& alarm = QueueAlarmAt(i);
+		if	(alarm.OriginatingSessionId() == aSessionId && alarm.Characteristics().IsSet(EAlarmCharacteristicsSessionSpecific))
+			DeQueueAlarm(alarm);
+		}
+	}
+
+/**
+ * Generates and returns the next valid alarm id.
+ */
+TAlarmId CASSrvAlarmQueue::NextFreeAlarmId()
+	{
+	if	(iNextFreeAlarmId == KMaxTInt)
+	    {
+	    iNextFreeAlarmId = 1; // Zero is special - "No alarm id"
+	    }
+	else
+	    {
+	    ++iNextFreeAlarmId;
+	    }
+
+	// Generate a unique alarm Id.
+	while(AlarmIdIsInUse(iNextFreeAlarmId))
+		{
+		if	(iNextFreeAlarmId == KMaxTInt)
+		    {
+		    iNextFreeAlarmId = 1;
+		    }
+		else
+		    {
+		    ++iNextFreeAlarmId;
+		    }
+		}
+
+	return iNextFreeAlarmId;
+	}
+
+/**
+ * Specify type of Store (or Internalize) operation wanted:
+ * 1. Externalize
+ *		- synchronous write of alarm queue and related information to file.
+ * 2. Internalize after Startup or Restore 
+ *		- synchronous read of alarm queue and related information from file.
+ * 3. Backup
+ */
+TInt CASSrvAlarmQueue::StartAlarmStoreOperation(TStoreOperation aStoreOperation)
+	{
+	TInt error = KErrNone;
+
+	switch (aStoreOperation)
+		{
+	    case EStoreInternalizeStartup:
+	        {
+            DEBUG_PRINT1(_L("> StartAlarmStoreOperation (InternalizeStartup)"));
+            // Can't have two concurrent Store operations
+            if (iStoreOperation != EStoreIdle)
+                error = KErrLocked;
+            break;
+	        }
+	    case EStoreInternalizeRestore:
+	        {
+            DEBUG_PRINT1(_L("> StartAlarmStoreOperation (InternalizeRestore)"));
+            // Check this is progression to Restore from Internalize
+            if (iStoreOperation != EStoreRestore)
+                error = KErrLocked;
+            break;
+	        }
+	    case EStoreExternalize:
+	        {
+            DEBUG_PRINT1(_L("> StartAlarmStoreOperation (Externalize)"));
+            // Can't have two concurrent Store operations
+            if (iStoreOperation != EStoreIdle)
+                error = KErrLocked;
+            break;
+	        }
+	    case EStoreBackup:
+	        {
+	        DEBUG_PRINT1(_L("> StartAlarmStoreOperation (Backup)"));
+	        // Can't have two concurrent Store operations
+	        if (iStoreOperation != EStoreIdle)
+	            error = KErrLocked;
+	        break;
+	        }
+	    case EStoreRestore:
+	        {
+	        DEBUG_PRINT1(_L("> StartAlarmStoreOperation (Restore)"));
+	        // Can't have two concurrent Store operations
+	        if (iStoreOperation != EStoreIdle)
+	            {
+	            error = KErrLocked;
+	            }
+	        else
+	            {
+	            // notify clients that Restore has started
+	            ServerData().AnyEventManager().MASAnyEventManagerObserverNotifyChanges(EAlarmChangeEventRestoreStarted, KNullAlarmId);
+	            }
+	        break;
+	        }
+	    case EStoreIdle:
+        default:
+            {
+            DEBUG_PRINT1(_L("> StartAlarmStoreOperation (Idle or other invalid)"));
+            error = KErrInUse;
+            __ASSERT_DEBUG(EFalse, ASSrvStaticUtils::Fault(ASSrvStaticUtils::EASSrvFaultStartInvalidAlarmStoreOperation));
+            break;
+	        }
+		}
+	if (!error)
+		{
+		iStoreOperation = aStoreOperation;
+		}
+	
+	DEBUG_PRINT2(_L("< StartAlarmStoreOperation result = %i"), error);
+	return error;
+	}
+
+#ifdef SYMBIAN_SYSTEM_STATE_MANAGEMENT
+/** 
+ * Handle a change to the next DST event
+ */
+void CASSrvAlarmQueue::HandleNextDSTChangeEventL()
+	{
+	UpdateRTC();
+	}
+
+/**
+ * Find the head wakeup alarm and queue set it with the real-time-clock.
+ */
+void CASSrvAlarmQueue::UpdateRTC()
+	{
+	// We always call UnsetWakeupAlarm even if we are going to call SetWakeupAlarm later because the RTC
+	// adaptation plug-in requires UnsetWakeupAlarm to be called before any call to SetWakeupAlarm.
+	TRequestStatus status;
+	iSsmRtcAdaptation.UnsetWakeupAlarm(status);
+	User::WaitForRequest(status);
+		
+	// loop through the alarm queue until the first wakeup alarm is found
+	TAlarmId headWakeupAlarmId = HeadWakeupAlarmId();
+	
+	DEBUG_PRINT2(_L("CASSrvAlarmQueue::UpdateRTC alarm count = %d"),QueueAlarmCount());
+	
+	if (headWakeupAlarmId != KNullAlarmId)
+		{
+		// There is a wakeup alarm
+		const TASSrvAlarm* headWakeupAlarm = QueueAlarmById(headWakeupAlarmId);
+		TTime wakeupAlarmTime(headWakeupAlarm->NextDueTime());
+		
+		if (headWakeupAlarm->IsFloating())
+			{
+			TTime nextDSTChangeUTC = iASSrvDSTChange->NextDSTChangeUTC();
+			
+			if (nextDSTChangeUTC != Time::NullTTime() && nextDSTChangeUTC < wakeupAlarmTime)
+				{
+				// The alarm is set to expire after a DST event so adjust the UTC time
+				// of the alarm to what it will be after the DST rollover
+				wakeupAlarmTime += iPreviousUtcOffset;
+				wakeupAlarmTime -= iASSrvDSTChange->NextUTCOffset();
+				}
+			}
+		
+		// Set the key value to 'EWakeupAlarmSet' so that listeners are notified of the presence of an active wakeup alarm.  
+		RProperty::Set(KAlarmServerPubSubCategory, KWakeupAlarmPubSubKey, EActiveWakeupAlarmSet );
+		DEBUG_PRINT1(_L("Set the KWakeupAlarmPubSubKey to EActiveWakeupAlarmSet"));
+	  		
+		// Set the Real Time Clock in UTC
+		TRequestStatus status;
+		TPckgC<TTime> wakeupAlarmTimePckg(wakeupAlarmTime);
+		iSsmRtcAdaptation.SetWakeupAlarm(wakeupAlarmTimePckg, status);
+		User::WaitForRequest(status);
+		}
+	else
+	    {
+	    // Notify the listeners that there is no head wake alarm present in the alarm queue 
+	    RProperty::Set(KAlarmServerPubSubCategory, KWakeupAlarmPubSubKey, EActiveNoWakeupAlarmsSet );
+	    DEBUG_PRINT1(_L("Set the KWakeupAlarmPubSubKey to EActiveNoWakeupAlarmsSet"));
+	    }
+	
+	}
+#endif
+
+/**
+ * End an Alarm Store Operation (Internalize, Externalize, Backup or Restore).
+ * Various tidying up including:
+ *   Finalise an Internalise, or throw it away. (depending on aError)
+ *   Process queued change events.
+ * Release the Access Token.
+ */
+void CASSrvAlarmQueue::EndAlarmStoreOperation(TInt aError)
+	{
+	switch (iStoreOperation)
+		{
+	    case EStoreIdle:
+	    default:
+	        {
+            DEBUG_PRINT1(_L("> EndAlarmStoreOperation (EStoreIdle or invalid)"));
+            __ASSERT_DEBUG(EFalse, ASSrvStaticUtils::Fault(ASSrvStaticUtils::EASSrvFaultEndInvalidAlarmStoreOperation));
+            break;
+	        }
+	    case EStoreInternalizeStartup:
+	    case EStoreInternalizeRestore:
+	        {
+	        DEBUG_PRINT2(_L("> EndAlarmStoreOperation (InternalizeXXX, aError = %i)"), aError);
+
+	        if (!aError)
+	            {
+	            // Alarm Queue is about to change
+	            // (stops Alarm Timer, currently notifying alarm, etc...)
+	            NotifyEvent(MASSrvAlarmQueueObserver::EASSrvAlarmQueueEventAlarmStartInternalize, KNullAlarmId);
+
+	            // Apply Internalize buffers for Alarm Queue, Alarm Data & Sound Intervals
+	            // Alarm Queue
+	            ApplyInternalizedData(ETrue);
+	            // Alarm Data Pool
+	            ServerData().DataPool().ApplyInternalizedData(ETrue);
+	            // Sound Settings
+	            ServerData().SoundSettings().ApplyInternalizedData(ETrue);
+
+	            // Notify observers about new queue
+	            const TAlarmId newHeadItemId = HeadAlarmId();
+	            if  (newHeadItemId != KNullAlarmId)
+	                {
+	                // Head item has changed. The timer will pick up the change and requeue itself
+	                // for the new alarm.
+	                NotifyEvent(MASSrvAlarmQueueObserver::EASSrvAlarmQueueEventHeadItemChanged, newHeadItemId);
+	                }
+
+	            // notify clients that Restore has completed
+	            if (iStoreOperation == EStoreInternalizeRestore)
+	                { 
+	                ServerData().AnyEventManager().MASAnyEventManagerObserverNotifyChanges(EAlarmChangeEventRestoreCompleted, KNullAlarmId);
+	                }
+
+#ifdef SYMBIAN_SYSTEM_STATE_MANAGEMENT              
+            
+	            else if (iStoreOperation == EStoreInternalizeStartup)
+	                {
+	                // When internalize happens on alarm server start-up publish the information 
+	                // whether we have an active alarm set or not
+	                DEBUG_PRINT1(_L("CASSrvAlarmQueue::EndAlarmStoreOperation Calls UpdateRTC after internalizing the queue on alarm server startup"));
+	                UpdateRTC();
+	                }
+            
+#endif            
+	            } // end of if block
+	        else
+	            {
+	            // Discard Internalize buffers for Alarm Queue, Alarm Data & Sound Intervals
+	            // Alarm Queue
+	            ApplyInternalizedData(EFalse);
+	            // Alarm Data Pool
+	            ServerData().DataPool().ApplyInternalizedData(EFalse);
+	            // Sound Settings
+	            ServerData().SoundSettings().ApplyInternalizedData(EFalse);
+
+	            // notify clients that Restore has failed
+	            if (iStoreOperation == EStoreInternalizeRestore)
+	                {
+	                ServerData().AnyEventManager().MASAnyEventManagerObserverNotifyChanges(EAlarmChangeEventRestoreFailed, KNullAlarmId);
+	                }
+	            }
+	        break;
+	        }
+	    case EStoreExternalize:
+	        {
+	        DEBUG_PRINT1(_L("> EndAlarmStoreOperation (Externalize)"));
+
+#ifdef SYMBIAN_SYSTEM_STATE_MANAGEMENT
+	        TRAP_IGNORE(UpdateRTC());
+#endif
+	        break;
+	        }
+	    case EStoreBackup:
+	        {
+	        DEBUG_PRINT1(_L("> EndAlarmStoreOperation (Backup)"));
+	        break;
+	        }
+	    case EStoreRestore:
+	        {
+	        DEBUG_PRINT1(_L("> EndAlarmStoreOperation (Restore)"));
+	        // notify clients that Restore has failed
+	        ServerData().AnyEventManager().MASAnyEventManagerObserverNotifyChanges(EAlarmChangeEventRestoreFailed, KNullAlarmId);
+	        break;
+	        }
+		} // end of switch-case block 
+	
+#ifdef SYMBIAN_SKIPPED_CALENDAR_ALARMS	
+	
+	// Republish the last alarmed instance parameters after internalizing on startup
+	if (iStoreOperation == EStoreInternalizeStartup)
+		{
+		PublishAlarmedInstanceParams(iLastAlarmedInstanceParams);
+		}
+	
+#endif		
+
+	iStoreOperation = EStoreIdle;
+	DEBUG_PRINT1(_L("< EndAlarmStoreOperation completed"));
+	return;
+	}
+
+/**
+ * Check that Alarm and Alarm Data is writable.
+ * Policy is to leave with KErrLocked if Restore is in progress.
+ */
+void CASSrvAlarmQueue::CheckAlarmQueueWritableL()
+	{
+	if (iStoreOperation == EStoreRestore 
+#ifdef SYMBIAN_SYSTEM_STATE_MANAGEMENT
+|| iServerWideData.ServerIsReadOnly()
+#endif
+      )
+		{
+		User::Leave(KErrLocked);
+		}
+	}
+
+/**
+ * Restore the queue from the specified stream.
+ */
+void CASSrvAlarmQueue::InternalizeL(RReadStream& aStream)
+	{
+	// First read the leading count which indicates how many alarms there
+	// are.
+	const TInt count = aStream.ReadInt32L();
+
+	// Read in the stream
+	TLinearOrder<TASSrvAlarm> order(ASSrvStaticUtils::CompareAlarms);
+
+	if (iInternalizeAlarmQueue.Count() != 0)
+	    {
+	    iInternalizeAlarmQueue.ResetAndDestroy();
+	    }
+
+	TTimeIntervalSeconds alarmExpireWindow = 0;
+
+	// what type of Internalize are we doing?
+	switch (iStoreOperation)
+		{
+	    case EStoreInternalizeStartup: // system startup
+	        {
+	        // 59s window for alarms in the past
+	        alarmExpireWindow = KAlarmServerStartupExpireWindowInSeconds;
+	        break;
+	        }
+	    case EStoreInternalizeRestore: // after a backup restore
+	        {
+            // 0 window for alarms in the past
+            // NB Notified Alarms stay Notified,
+            // but Notifying Alarms become Notified
+            // This is reasonable as a Restore of alarms is a dramatic thing to do.
+            alarmExpireWindow = 0;
+            break;
+	        }
+	    default:
+	        {
+	        __ASSERT_ALWAYS(EFalse, ASSrvStaticUtils::Panic(ASSrvStaticUtils::EASSrvPanicInternalizeTypeInvalid));
+	        break;
+	        }
+		}
+	
+	RPointerArray<TASSrvAlarm> alarmArray;
+	CleanupResetAndDestroyPushL(alarmArray);
+		
+	for(TInt j=0; j<count; j++)
+		{
+		TASSrvAlarm* alarm = new(ELeave) TASSrvAlarm(ServerData());
+		CleanupStack::PushL(alarm);
+		aStream >> *alarm;
+		alarmArray.AppendL(alarm);
+		CleanupStack::Pop();
+		}
+		
+	// Read in the next valid id
+	iInternalizeNextFreeAlarmId = aStream.ReadInt32L();
+	
+	// Internalize the UTC offset the last time the queue was run.
+	TInt32 tempInt;
+	aStream >> tempInt;
+	TTimeIntervalSeconds currentOffset = User::UTCOffset();
+	TTimeIntervalSeconds cachedOffset(tempInt);
+	
+	TTime previousDSTChangeUTC = iASSrvDSTChange->PreviousDSTChangeUTC();
+	TTimeIntervalMinutes previousUTCOffset = iASSrvDSTChange->PreviousUTCOffset();
+	TTime localTimeBeforeDSTChange = previousDSTChangeUTC + previousUTCOffset;
+	TTime localTimeAfterDSTChange = previousDSTChangeUTC + currentOffset;
+	
+	for(TInt i=0; i<count; i++)
+		{
+		// Internalize the alarm (replacing any previous details)
+		TASSrvAlarm* alarm ;
+		alarm = alarmArray[i];
+		// Set id to KNullAlarmId to prevent notifications when
+		// the alarm is enabled
+		const TAlarmId originalId = alarm->Id();
+		alarm->Id()=KNullAlarmId;
+
+		// Validate the alarm, retaining the alarm status (enabled or disabled)
+		// - we only add valid alarms to the queue.
+		const TBool KAllowBlanketInThePastOnceOnlyAlarms = EFalse; // Default value
+		const TBool KEnableAlarm = alarm->Status()==EAlarmStatusEnabled;
+		//An alarm set to 'repeat next 24 hour' can be missed while the alarm server is inactive.
+		//Therefore it should be changed to 'repeat once' before it is restored to the alarm queue
+		//so that it is not incorrectly resheduled to notify in the next 24 hours
+		if(alarm->RepeatDefinition() == EAlarmRepeatDefintionRepeatNext24Hours)
+			{
+			alarm->RepeatDefinition() = EAlarmRepeatDefintionRepeatOnce;
+			}
+			
+		TTime previousAlarmLocalTime = alarm->NextDueTime() + previousUTCOffset;
+		TBool alarmInDSTChangeGap = (previousDSTChangeUTC != Time::NullTTime() // make sure DST change info has been published
+										&& (previousAlarmLocalTime >= localTimeBeforeDSTChange && previousAlarmLocalTime < localTimeAfterDSTChange ) );
+		
+		TTimeIntervalSeconds missingTime;
+		localTimeAfterDSTChange.SecondsFrom(localTimeBeforeDSTChange, missingTime);
+		
+		// Increase the allowable window if this alarm was due to go off in the DST change gap
+		TTimeIntervalSeconds thisAlarmsExpireWindow = alarmInDSTChangeGap ? alarmExpireWindow.Int() + missingTime.Int() : alarmExpireWindow;
+		
+		// Update the due time of floating alarms during a DST event. This will ensure that alarms
+		// are not invalidated by ValidateAndEnable().
+		TTimeIntervalSeconds offsetDifference;
+	
+		if(alarm->IsFloating())
+			{
+			if (cachedOffset != currentOffset)
+				{
+				// Calculate the change in the offset.
+				offsetDifference = (currentOffset.Int() - cachedOffset.Int());
+				alarm->NextDueTime() -= offsetDifference;
+				alarm->OriginalExpiryTime() -= offsetDifference;
+				}	
+			}
+	
+		const TInt error = alarm->ValidateAndEnable(thisAlarmsExpireWindow, KAllowBlanketInThePastOnceOnlyAlarms, KEnableAlarm);
+		
+		if (alarmInDSTChangeGap)
+			{
+			// The local alarm time was set during the missing DST changeover gap
+			// this repeat should actually appear an hour later than 
+			alarm->NextDueTime() += missingTime;
+			}
+		
+		if	(error != KErrArgument)
+			{
+			// Alarm is okay, so insert it into the queue in the 
+			// right position
+			alarm->Id()=originalId;
+			const TInt error = iInternalizeAlarmQueue.InsertInOrderAllowRepeats(alarm, order);
+			User::LeaveIfError(error);
+			alarmArray[i] = NULL;
+			}
+		else
+			{
+			delete alarmArray[i];
+			alarmArray[i] = NULL;
+			}
+		}
+	CleanupStack::PopAndDestroy(); //alarmArray
+	ServerData().CachedUtcOffset() = currentOffset;
+	}
+
+/**
+ * Whether to use the Internalize buffer, or throw it away
+ */
+void CASSrvAlarmQueue::ApplyInternalizedData(TBool aUseNewData)
+	{
+	DEBUG_PRINT2(_L("** ApplyInternalizedData(%u)"), aUseNewData);
+
+	if (aUseNewData)
+		{
+		// Internalize Success
+
+		// Replace all alarms with those from the stream
+		// Assign new queue to replace old one
+		ReplaceQueueWithInternalizedQueue();
+
+		iNextFreeAlarmId = iInternalizeNextFreeAlarmId;
+	
+		// Remove any which are too old
+		RemoveDeadAlarms();
+		// Check whether the UTC offset has changed since the last time the Alarm Server
+		// was running, and update floating alarm due times if it has.
+		UpdateFloatingDueTimes();
+		}
+	else 
+		{
+		// Internalize Failure
+		iInternalizeAlarmQueue.ResetAndDestroy();
+		
+		// Set the previous UTC offset to the current UTC offset, since there was no
+		// previous offset internalised.
+	    ServerData().CachedUtcOffset() = User::UTCOffset();
+		}
+	}
+
+/**
+ * Update the due times of floating alarms due to a locale or DST change.
+ * 
+ * @internalComponent
+ */
+void CASSrvAlarmQueue::UpdateFloatingDueTimes()
+	{
+	// Has the UTC offset changed due to a Locale change?
+	TTimeIntervalSeconds offset = User::UTCOffset();
+	
+	if (ServerData().CachedUtcOffset() != offset)
+		{
+		// Update the locally set alarm due times because the UTC offset has changed.
+		
+		// Calculate the change in the offset.
+		TTimeIntervalSeconds offsetDifference(offset.Int() - ServerData().CachedUtcOffset().Int());
+
+		// Get the head id so that we know if the queue has changed
+		const TAlarmId headItemId = HeadAlarmId();
+		
+		// Cycle through all alarms.
+		TInt count = QueueAlarmCount();
+		TBool orderChanged = EFalse;
+		
+		for (TInt i = 0; i < count; i++)
+			{
+			TASSrvAlarm& alarm = QueueAlarmAt(i);
+			
+			// Check whether the alarm is floating.
+			if (alarm.IsFloating())
+				{
+				// Update the due time of the alarm, since it is floating.
+				TTime oldDueTime = alarm.NextDueTime();
+				
+				// Subtract the change in offset from the due time to get the new due time.
+				TTime newDueTime = oldDueTime - offsetDifference;
+				
+				// Set the new due time of the alarm.
+				alarm.NextDueTime()=newDueTime;
+				
+				// Also, shift the original expiry time, to ensure that repeating
+				// alarms repeat correctly.
+				newDueTime = alarm.OriginalExpiryTime() - offsetDifference;
+				
+				// Shift the original ExpiryTime.
+				alarm.OriginalExpiryTime()=newDueTime;
+				orderChanged = ETrue;
+				}
+			}
+			
+		// Check whether the order of the alarm queue may have changed.
+		if (orderChanged)
+			{
+			// Re-order the alarm queue.
+			TLinearOrder<TASSrvAlarm> order(ASSrvStaticUtils::CompareAlarms);
+			iAlarms.Sort(order);
+			
+			// Did the head item change?
+			const TAlarmId newHeadItemId = HeadAlarmId();
+			
+			if	(newHeadItemId != headItemId)
+				{
+				// Head item has changed. The timer will pick up the change and requeue itself
+				// for the new alarm.
+				NotifyEvent(MASSrvAlarmQueueObserver::EASSrvAlarmQueueEventHeadItemChanged, newHeadItemId);
+				}
+			}
+
+		// Set the previous UTC offset to the current UTC offset.
+		ServerData().CachedUtcOffset() = offset;
+		}
+	}
+
+/** 
+ * Publish skipped alarm data.
+ * 
+ * @internalComponent
+ */
+void CASSrvAlarmQueue::PublishSkippedAlarm(TBool aSkippedCalAlarm)
+	{
+	// Define the property before use
+	TInt err = RProperty::Define(KAlarmServerPubSubCategory, KMissingAlarmPubSubKey, RProperty::EByteArray, KReadPolicy, KWritePolicy);
+	
+	// If the define completed successfully, create the data and publish
+	if (err == KErrNone || err == KErrAlreadyExists)
+		{
+		TMissedAlarmPubSubData pubSubData;
+		pubSubData.iTimeOfChangeUtc.UniversalTime(); 
+		if(aSkippedCalAlarm)
+			{
+			// There are skipped alarms after a system time or time zone change
+			pubSubData.iValue = 2;
+			}
+		else
+			{
+			// There was a time zone change, but no alarms were skipped
+			pubSubData.iValue = 1;	
+			}
+		
+		TPckgBuf<TMissedAlarmPubSubData> pubSubBuf(pubSubData);
+		RProperty::Set(KAlarmServerPubSubCategory, KMissingAlarmPubSubKey, pubSubBuf);	
+		}
+	}
+
+#ifdef SYMBIAN_SKIPPED_CALENDAR_ALARMS
+
+/**
+ * Publish data about the skipped alarm.
+ * 
+ * @internalComponent
+ */
+void CASSrvAlarmQueue::PublishAlarmedInstanceParams(const TTime& aFirstSkippedAlarmLocalTime, TBool aSystemTimeChange)
+	{
+	// Create the data structure before publishing
+	TASShdAlarmedInstanceParams alarmedInstanceParams;
+	alarmedInstanceParams.iLocalStartTime = aFirstSkippedAlarmLocalTime;
+	alarmedInstanceParams.iLocalEndTime = ASSrvStaticUtils::LocalTimeNow();
+	alarmedInstanceParams.iTimeType = aSystemTimeChange ? EFloatingOrFixed : EFloating;
+	
+	PublishAlarmedInstanceParams(alarmedInstanceParams);
+	}
+
+/**
+ * Publish data about the skipped alarm.
+ * 
+ * @internalComponent
+ */
+void CASSrvAlarmQueue::PublishAlarmedInstanceParams(const TASShdAlarmedInstanceParams& aAlarmedInstanceParams)
+	{
+	// Define the property before use
+	TInt err = RProperty::Define(KAlarmServerPubSubCategory, KSkippedAlarmInstancesPubSubKey, RProperty::EByteArray, KReadPolicy, KWritePolicy);
+	
+	// If the define completed successfully, publish
+	if (err == KErrNone || err == KErrAlreadyExists)
+		{		
+		TPckgBuf<TASShdAlarmedInstanceParams> pubSubBuf(aAlarmedInstanceParams);
+		RProperty::Set(KAlarmServerPubSubCategory, KSkippedAlarmInstancesPubSubKey, pubSubBuf);
+		iLastAlarmedInstanceParams = aAlarmedInstanceParams;
+		}	
+	}
+#endif
+
+/**
+ * Store the queue to the specified stream.
+ */
+void CASSrvAlarmQueue::ExternalizeL(RWriteStream& aStream) const
+	{
+	const TInt count = QueueAlarmCount();
+	aStream.WriteInt32L(count);
+	//
+	for(TInt i=0; i<count; i++)
+		{
+		const TASSrvAlarm& alarm = QueueAlarmAt(i);
+		aStream << alarm;
+		}
+	aStream.WriteInt32L(iNextFreeAlarmId);
+	
+	// Externalize the UTC offset.
+	TInt32 tempInt = ServerData().CachedUtcOffset().Int();
+	aStream << tempInt;
+	}
+
+#ifdef SYMBIAN_SKIPPED_CALENDAR_ALARMS
+/**
+ * Externalize the last skipped agenda alarm data
+ */
+void CASSrvAlarmQueue::ExternalizeLastAlarmedInstanceParamsL(RWriteStream& aStream) const
+	{
+	TPckgBuf<TASShdAlarmedInstanceParams> paramsBuf(iLastAlarmedInstanceParams);
+	aStream << paramsBuf;
+	
+	// Write two words reserved for future use
+	aStream.WriteInt32L(0);
+	aStream.WriteInt32L(0);
+	}
+
+/**
+ * Internalize the last published skipped agenda alarm data
+ */
+void CASSrvAlarmQueue::InternalizeLastAlarmedInstanceParamsL(RReadStream& aStream)
+	{
+	TPckgBuf<TASShdAlarmedInstanceParams> paramsBuf;
+	aStream >> paramsBuf;
+	iLastAlarmedInstanceParams = paramsBuf();
+	
+	// Read two words reserved for future use
+	aStream.ReadInt32L();
+	aStream.ReadInt32L();	
+	}
+#endif
+
+/**
+ * Called when the status of one or more alarms changes
+ */
+void CASSrvAlarmQueue::HandleAlarmStatusChanged(TAlarmId aAlarmThatChangedStatus, TAlarmStatus aOldStatus)
+	{
+	// Resort the queue. We need to do this for alarms which may, for example,
+	// have a repeat definition and was disabled and has been re-enabled but the
+	// original expiry time has since passed.  The alarm's next due time has
+	// been updated but the queue does not yet reflect this.
+	TLinearOrder<TASSrvAlarm> order(ASSrvStaticUtils::CompareAlarms);
+	iAlarms.Sort(order);
+
+	// Called whenever an alarm changes state. We need to update the alarm timer.
+	const TASSrvAlarm* alarm = QueueAlarmById(aAlarmThatChangedStatus);
+	//
+	const TAlarmId newHeadAlarmId = HeadAlarmId();
+	const TAlarmId timerAlarmId = ServerData().Timer().NextDueAlarmId();
+	//
+	if	(timerAlarmId != newHeadAlarmId || aAlarmThatChangedStatus == timerAlarmId)
+		{
+		// The head item has changed.
+		NotifyEvent(MASSrvAlarmQueueObserver::EASSrvAlarmQueueEventHeadItemChanged, newHeadAlarmId);
+		}
+
+	// Notify observers
+	NotifyEvent(MASSrvAlarmQueueObserver::EASSrvAlarmQueueEventAlarmChanged, aAlarmThatChangedStatus);
+	NotifyAlarmObserverEvent(MASSrvAlarmObserver::EAlarmObserverStatusChanged, *alarm, static_cast<TInt>(aOldStatus));
+
+	// Notify change
+	ServerData().AnyEventManager().MASAnyEventManagerObserverNotifyChanges(EAlarmChangeEventStatus, aAlarmThatChangedStatus);
+	}
+
+/**
+ * Called when the state of one or more alarms changes
+ */
+void CASSrvAlarmQueue::HandleAlarmStateChanged(TAlarmId aAlarmThatChangedState, TAlarmState aOldState)
+	{
+	// Resort the queue. We need to do this for alarms which have now become snoozed or
+	// alarms which were notified but are now re-queued because of their repeat definitions.
+	TLinearOrder<TASSrvAlarm> order(ASSrvStaticUtils::CompareAlarms);
+	iAlarms.Sort(order);
+
+	// We need to update the alarm timer if the head alarm is different to
+	// the old one.
+	CASSrvAlarmTimer& timer = ServerData().Timer();
+	//
+	const TAlarmId newHeadAlarmId = HeadAlarmId();
+	const TAlarmId timerAlarmId = timer.NextDueAlarmId();
+	//
+	if	(timerAlarmId != newHeadAlarmId)
+		{
+		// The head item has changed.
+		NotifyEvent(MASSrvAlarmQueueObserver::EASSrvAlarmQueueEventHeadItemChanged, newHeadAlarmId);
+		}
+	else if (newHeadAlarmId != KNullAlarmId)
+		{
+		// It is possible for the head alarm to change it's due time.
+		// In this case, the head alarm id is the same, but the times 
+		// are different.
+		const TASSrvAlarm* newHeadAlarm = QueueAlarmById(newHeadAlarmId);
+		const TTime& timerDueTime = timer.NextDueAlarmOriginalExpiryTime();
+		if	(timerAlarmId == newHeadAlarmId && timerDueTime != newHeadAlarm->NextDueTime())
+			{
+			// Alarm id hasn't changed, but the due time has
+			NotifyEvent(MASSrvAlarmQueueObserver::EASSrvAlarmQueueEventHeadItemChanged, newHeadAlarmId);
+			}
+		}
+
+	// Notify observers
+	NotifyEvent(MASSrvAlarmQueueObserver::EASSrvAlarmQueueEventAlarmChanged, aAlarmThatChangedState);
+	NotifyAlarmObserverEvent(MASSrvAlarmObserver::EAlarmObserverStateChanged, *QueueAlarmById(aAlarmThatChangedState), static_cast<TInt>(aOldState));
+
+	// Notify change
+	ServerData().AnyEventManager().MASAnyEventManagerObserverNotifyChanges(EAlarmChangeEventState, aAlarmThatChangedState);
+	}
+
+/**
+ * Called when the state of one or more alarms changes
+ */
+void CASSrvAlarmQueue::HandleAlarmCharacteristicsChanged(TAlarmId aAlarmThatChangedCharacteristics, TAlarmCharacteristicsFlags /*aOldCharacteristics*/)
+	{
+	// Notify observers
+	NotifyEvent(MASSrvAlarmQueueObserver::EASSrvAlarmQueueEventAlarmChanged, aAlarmThatChangedCharacteristics);
+	/** 
+	 * Alarm Observers don't currently need to know about this change.
+	 * If this changes this is the place to do something like:
+	 * NotifyAlarmObserverEvent(MASSrvAlarmObserver::EAlarmObserverCharacteristicsChanged, 
+	 *     QueueAlarmById(aAlarmThatChangedCharacteristics),
+	 *     static_cast<TInt>(aOldCharacteristics.Value()));
+	 */
+
+	// Notify change
+	ServerData().AnyEventManager().MASAnyEventManagerObserverNotifyChanges(EAlarmChangeEventCharacteristics, aAlarmThatChangedCharacteristics);
+	}
+
+#ifdef SYMBIAN_SYSTEM_STATE_MANAGEMENT
+void CASSrvAlarmQueue::HandleWakeupChanged(TAlarmId aAlarmThatChangedWakeup)
+	{
+	// Notify observers
+	NotifyEvent(MASSrvAlarmQueueObserver::EASSrvAlarmQueueEventAlarmChanged, aAlarmThatChangedWakeup);
+	}
+#endif
+
+#ifdef SYMBIAN_ALARM_REPEAT_EXTENSIONS
+void CASSrvAlarmQueue::HandleAlarmDaysChanged(TAlarmId aAlarmThatChangedDays)
+	{
+	// Check that the alarm has been queued (which we infer by the alarm having an id).
+	if (aAlarmThatChangedDays == KNullAlarmId)
+		{
+		return;
+		}
+	
+	// Resort the queue.
+	TLinearOrder<TASSrvAlarm> order(ASSrvStaticUtils::CompareAlarms);
+	iAlarms.Sort(order);
+
+	// Get the id of the head alarm.
+	const TAlarmId headAlarmId = HeadAlarmId();
+
+	// Get the id of the alarm associated with the alarm timer.
+	CASSrvAlarmTimer& timer = ServerData().Timer();
+	const TAlarmId timerAlarmId = timer.NextDueAlarmId();
+
+	// Assume the head item will not change.
+	TBool headItemChanged = EFalse;
+	
+	if (headAlarmId != timerAlarmId)
+		{
+		headItemChanged = ETrue;
+		}
+	else
+		{
+		if (headAlarmId != KNullAlarmId)
+			{
+			const TASSrvAlarm* headAlarm = QueueAlarmById(headAlarmId);
+			const TTime& timerDueTime = timer.NextDueAlarmOriginalExpiryTime();
+
+			// It is possible for the head alarm to change its due time.  In
+			// this case the alarm timer will no longer be correct.
+			if (headAlarm->NextDueTime() != timerDueTime)
+				{
+				headItemChanged = ETrue;
+				}
+			}
+		}
+	
+	if (headItemChanged)
+		{
+		NotifyEvent(MASSrvAlarmQueueObserver::EASSrvAlarmQueueEventHeadItemChanged, headAlarmId);
+		}
+	}
+#endif
+
+void CASSrvAlarmQueue::HandleAlarmDataChanged(TAlarmId aAlarmThatChangedData)
+	{
+	// Notify observers
+	NotifyEvent(MASSrvAlarmQueueObserver::EASSrvAlarmQueueEventAlarmChanged, aAlarmThatChangedData);
+	}
+
+/**
+ * Returns a boolean indicating whether or not the specified alarm id
+ * is currently being used by another alarm.
+ */
+TBool CASSrvAlarmQueue::AlarmIdIsInUse(TAlarmId aAlarmId) const
+	{
+	const TInt count = QueueAlarmCount();
+	for(TInt i=0; i<count; i++)
+		{
+		if	(QueueAlarmAt(i).Id() == aAlarmId)
+			return ETrue;
+		}
+	return EFalse;
+	}
+
+/**
+ * Remove any alarms from the queue that have notified already. Anything over 7 days old should be removed.
+ */
+void CASSrvAlarmQueue::RemoveDeadAlarms()
+	{
+	const TInt count = QueueAlarmCount();
+	const TTime timeNow(ASSrvStaticUtils::UtcTimeNow());
+	for(TInt i=count-1; i>=0; i--)
+		{
+		TASSrvAlarm& alarm = QueueAlarmAt(i);
+		const TTimeIntervalDays daysSinceLastExpired(alarm.NextDueTime().DaysFrom(timeNow));
+		if	(alarm.State() == EAlarmStateNotified && Abs(daysSinceLastExpired.Int()) >= KASSrvNumberOfDaysInOneWeek)
+			alarm.DeQueue();
+		}
+	}
+
+/**
+ * Notify all alarm observers about the specified event.
+ */
+void CASSrvAlarmQueue::NotifyAlarmObserverEvent(MASSrvAlarmObserver::TObserverEvent aEvent, const TASSrvAlarm& aAlarm, TInt aEventSpecificData)
+	{
+	const TInt count = iAlarmObservers.Count();
+	for(TInt i=0; i<count; i++)
+		{
+		TASSrvAlarmObserverMapplet& mapplet = iAlarmObservers[i];
+		if	(mapplet.IsEnabled())
+			mapplet.Observer().MASSrvAlarmObsHandleEvent(aEvent, aAlarm, aEventSpecificData);
+		}
+	}
+
+/**
+ * @see MASSrvAlarmInfoProvider
+ */
+TInt CASSrvAlarmQueue::ASSrvAlarmInfoCount() const
+	{
+	return QueueAlarmCount();
+	}
+
+/**
+ * @see MASSrvAlarmInfoProvider
+ */
+void CASSrvAlarmQueue::ASSrvAlarmInfoAt(TInt aIndex, TASSrvAlarm& aAlarm) const
+	{
+	aAlarm = QueueAlarmAt(aIndex);
+	}
+
+/**
+ * Register for notifications when the alarm queue order changes
+ */
+void CASSrvAlarmQueue::NotificationPoolChangeL(MASSrvAlarmQueueObserver& aObserver)
+	{
+	User::LeaveIfError(iNotificationList.InsertInAddressOrder(&aObserver));
+	}
+
+/**
+ * Deregister for notifications when the alarm queue order changes
+ */
+void CASSrvAlarmQueue::NotificationPoolChangeCancel(MASSrvAlarmQueueObserver& aObserver)
+	{
+	TInt index = KErrNotFound;
+	const TInt error = iNotificationList.FindInAddressOrder(&aObserver, index);
+	if	(error != KErrNotFound)
+		iNotificationList.Remove(index);
+	}
+
+/**
+ * Return the number of alarms currently maintained within the alarm pool
+ */
+TInt CASSrvAlarmQueue::QueueAlarmCount() const
+	{
+	return iAlarms.Count();
+	}
+
+/**
+ * Return a reference to the alarm at the specified reference
+ */
+TASSrvAlarm& CASSrvAlarmQueue::QueueAlarmAt(TInt aIndex)
+	{
+	return *iAlarms[aIndex];
+	}
+
+/**
+ * Return a constant reference to the alarm at the specified index
+ */
+const TASSrvAlarm& CASSrvAlarmQueue::QueueAlarmAt(TInt aIndex) const
+	{
+	return *iAlarms[aIndex];
+	}
+
+/**
+ * Return a copy of an alarm.
+ */
+TInt CASSrvAlarmQueue::QueueAlarmById(TAlarmId aId, TASSrvAlarm& aAlarm) const
+	{
+	const TInt count = iAlarms.Count();
+	for(TInt i=0; i<count; i++)
+		{
+		const TASSrvAlarm& alarm = QueueAlarmAt(i);
+		if	(alarm.Id() == aId)
+			{
+			aAlarm = alarm;
+			return KErrNone;
+			}
+		}
+
+	// No alarm available
+	return KErrNotFound;
+	}
+
+/**
+ * Returns a reference to a real alarm with the specified id.
+ */
+TASSrvAlarm* CASSrvAlarmQueue::QueueAlarmById(TAlarmId aId)
+	{
+	const TInt count = iAlarms.Count();
+	for(TInt i=0; i<count; i++)
+		{
+		if	(iAlarms[i]->Id() == aId)
+			return iAlarms[i];
+		}
+	
+	return NULL;
+	}
+
+/**
+ * Returns a constant reference to a real alarm with the specified id.
+ */
+const TASSrvAlarm* CASSrvAlarmQueue::QueueAlarmById(TAlarmId aId) const
+	{
+	const TInt count = iAlarms.Count();
+	for(TInt i=0; i<count; i++)
+		{
+		if	(iAlarms[i]->Id() == aId)
+			return iAlarms[i];
+		}
+		
+	return NULL;
+	}
+
+/**
+ * Returns a reference to a real alarm with the specified id.
+ */
+TASSrvAlarm& CASSrvAlarmQueue::QueueAlarmByIdL(TAlarmId aId)
+	{
+	TASSrvAlarm* alarm = QueueAlarmById(aId);
+	if (!alarm)
+		{
+		User::Leave(KErrNotFound);
+		}
+	return *alarm;
+	}
+
+/**
+ * Returns a constant reference to a real alarm with the specified id.
+ */
+const TASSrvAlarm& CASSrvAlarmQueue::QueueAlarmByIdL(TAlarmId aId) const
+	{
+	const TASSrvAlarm* alarm = QueueAlarmById(aId);
+	if (!alarm)
+		{
+		User::Leave(KErrNotFound);
+		}
+	return *alarm;
+	}
+
+/**
+ * Notify observers about an event
+ */
+void CASSrvAlarmQueue::NotifyEvent(MASSrvAlarmQueueObserver::TASSrvAlarmQueueEvent aEvent, TAlarmId aAlarmId)
+	{
+	const TInt count = iNotificationList.Count();
+	for(TInt i=0; i<count; i++)
+		{
+		iNotificationList[i]->MAlarmQueueObserverHandleEvent(aEvent, aAlarmId);
+		}
+	}
+
+/**
+ * Replace one queue with another
+ */
+void CASSrvAlarmQueue::ReplaceQueueWithInternalizedQueue()
+	{
+	const TInt count = iAlarms.Count();
+	for(TInt i=0; i<count; i++)
+		{
+		// If there was a notification request then we complete it
+		iAlarms[i]->CancelSessionAlarm();
+		}
+
+	// free memory used by old queue
+	iAlarms.ResetAndDestroy();
+
+	// assign from new buffer
+	iAlarms = iInternalizeAlarmQueue;
+
+	// re-initialise source pointer array, by re-running its constructor
+	new(&iInternalizeAlarmQueue) RPointerArray<TASSrvAlarm>;
+	}
+
+/**
+ * Returns ETrue if there is at least one alarm in the "waiting to notify
+ * state"
+ */
+TBool CASSrvAlarmQueue::HaveAdditionalAlarmsToNotify()
+	{
+	// Create & open primary iterator
+	RASSrvIteratorByState primaryIterator(*this, EAlarmStateWaitingToNotify);
+	primaryIterator.Open();
+
+	// Create and attach secondary iterator
+	RASSrvIteratorByStatus secondaryIterator(*this, EAlarmStatusEnabled);
+	primaryIterator.IteratorAttach(secondaryIterator);
+
+	return primaryIterator.NextAlarmAvailable();
+	}
+
+/**
+ * Returns the number of alarms which are enabled and in the "waiting to
+ * notify state"
+ */
+TInt CASSrvAlarmQueue::NumberOfAlarmsPendingNotification()
+	{
+	// Create & open primary iterator
+	RASSrvIteratorByState primaryIterator(*this, EAlarmStateWaitingToNotify);
+	primaryIterator.Open();
+
+	// Create and attach secondary iterator
+	RASSrvIteratorByStatus secondaryIterator(*this, EAlarmStatusEnabled);
+	primaryIterator.IteratorAttach(secondaryIterator);
+
+	TInt count = 0;
+	while(primaryIterator.NextAlarmAvailable())
+		{
+		primaryIterator.NextAlarm();
+		++count;
+		}
+
+	return count;
+	}
+
+/**
+ * Return a reference to the alarm which is next awaiting notification
+ */	
+TASSrvAlarm& CASSrvAlarmQueue::NextAlarmWaitingForNotification()
+	{
+	// Create & open primary iterator
+	RASSrvIteratorByState primaryIterator(*this, EAlarmStateWaitingToNotify);
+	primaryIterator.Open();
+
+	// Create and attach secondary iterator
+	RASSrvIteratorByStatus secondaryIterator(*this, EAlarmStatusEnabled);
+	primaryIterator.IteratorAttach(secondaryIterator);
+
+	// The most recent alarm is at the head of the queue.
+	// This will be the next one awaiting notification.
+	TAlarmId idOfFirstAlarm = KNullAlarmId;
+	if(primaryIterator.NextAlarmAvailable())
+		{
+		idOfFirstAlarm = primaryIterator.NextAlarm().Id();
+		}
+	
+	__ASSERT_ALWAYS(idOfFirstAlarm != KNullAlarmId, ASSrvStaticUtils::Panic(ASSrvStaticUtils::EASSrvPanicIteratorAlarmIdNull));
+	return *QueueAlarmById(idOfFirstAlarm);
+	}