--- /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);
+ }