// 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
User::LeaveIfError(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);
}