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