commonappservices/alarmserver/Server/Source/ASSrvTimer.cpp
changeset 0 2e3d3ce01487
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/commonappservices/alarmserver/Server/Source/ASSrvTimer.cpp	Tue Feb 02 10:12:00 2010 +0200
@@ -0,0 +1,276 @@
+// 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 "ASSrvTimer.h"
+
+// System includes
+#include <e32base.h>
+
+// User includes
+#include "ASSrvStaticUtils.h"
+#include "ASSrvAlarmQueue.h"
+#include "ASSrvTimerObserver.h"
+#include "ASSrvServerWideData.h"
+#include "ASSrvAnyEventManager.h"
+
+//
+// ----> CASSrvAlarmTimer (source)
+//
+
+//*************************************************************************************
+CASSrvAlarmTimer::CASSrvAlarmTimer(CASSrvServerWideData& aServerWideData)
+:	CTimer(CActive::EPriorityStandard), iServerWideData(aServerWideData), iNextDueAlarmId(KNullAlarmId),
+	iInvalidatedByInternalize(EFalse)
+	{
+	CActiveScheduler::Add(this);
+	}
+
+
+//*************************************************************************************
+CASSrvAlarmTimer::~CASSrvAlarmTimer()
+	{
+	ServerData().Queue().NotificationPoolChangeCancel(*this);
+	iNotificationList.Close();
+	}
+
+
+//*************************************************************************************
+void CASSrvAlarmTimer::ConstructL()
+	{
+	CTimer::ConstructL();
+	ServerData().Queue().NotificationPoolChangeL(*this);
+	}
+
+
+//*************************************************************************************
+CASSrvAlarmTimer* CASSrvAlarmTimer::NewL(CASSrvServerWideData& aServerWideData)
+	{
+	CASSrvAlarmTimer* self = new(ELeave) CASSrvAlarmTimer(aServerWideData);
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+
+//
+//
+//
+
+
+//*************************************************************************************
+/**
+ * Request notifications when an alarm expires
+ */
+void CASSrvAlarmTimer::NotifyAlarmExpiredL(MASSrvAlarmTimerObserver& aObserver)
+	{
+	User::LeaveIfError(iNotificationList.InsertInAddressOrder(&aObserver));
+	}
+
+
+//*************************************************************************************
+/**
+ * Cancel a previous notification request
+ */
+void CASSrvAlarmTimer::NotifyAlarmExpiredCancel(MASSrvAlarmTimerObserver& aObserver)
+	{
+	TInt index = KErrNotFound;
+	const TInt error = iNotificationList.FindInAddressOrder(&aObserver, index);
+	if	(error != KErrNotFound)
+		iNotificationList.Remove(index);
+	}
+
+
+//
+//
+//
+
+
+//*************************************************************************************
+/**
+ * @see MASSrvAlarmQueueObserver
+ */
+void CASSrvAlarmTimer::MAlarmQueueObserverHandleEvent(TASSrvAlarmQueueEvent aEvent, TAlarmId aAlarmId)
+	{
+	const TAlarmId previousDueAlarmId = iNextDueAlarmId;
+
+	/* There two interesting events that we should look for:
+	 * Alarm Queue Start Internalize - everything we know is now invalid
+	 * Alarm Queue Head Item Changed - stop what we are doing, and see if/when
+	 *   the next alarm is due.
+	 */
+	switch (aEvent)
+		{
+	case EASSrvAlarmQueueEventAlarmStartInternalize:
+		// stop processing alarm queue, (until a new head alarm appears)
+		Cancel();
+		// RunL could have started already (it waits for an Access Token)
+		// and would be processing an invalid head alarm!
+		iInvalidatedByInternalize = ETrue;
+		break;
+
+	case EASSrvAlarmQueueEventHeadItemChanged:
+		{
+	// The next alarm has changed.
+	Cancel();
+
+		// head alarm is valid
+		iInvalidatedByInternalize = EFalse;
+
+	// Set ourselves up for the next alarm, assuming there is one.
+	CASSrvAlarmQueue& queue = ServerData().Queue();
+	const TASSrvAlarm* nextDueAlarm = queue.HeadAlarm();
+	if	(nextDueAlarm && aAlarmId != KNullAlarmId)
+		{
+		__ASSERT_DEBUG(nextDueAlarm->Id() != KNullAlarmId, ASSrvStaticUtils::Panic(ASSrvStaticUtils::EASSrvPanicQueueAlarmIdNull));
+		
+		// Convert alarm time to local time
+		TTime dueTime = nextDueAlarm->NextDueTime();
+		
+		// Get current time
+		const TTime now(ASSrvStaticUtils::UtcTimeNow());
+		
+		// Expire now (if the alarm is in the past), or when the alarm
+		// is really due (if it's in the future).
+		TTime t = dueTime > now? dueTime : now;
+
+
+		AtUTC(t);					
+
+		// Update next due alarm id and original due time
+		iOriginalExpiryTimeForAlarm = dueTime;
+		iNextDueAlarmId = aAlarmId;
+		}
+		}
+		break;
+
+	default: // don't care about other events
+		return;
+		}
+
+	// Notify change
+	if	(iNextDueAlarmId != previousDueAlarmId)
+		ServerData().AnyEventManager().MASAnyEventManagerObserverNotifyChanges(EAlarmChangeEventHeadQueueItemChanged, iNextDueAlarmId);
+	}
+
+
+//
+//
+//
+
+
+//*************************************************************************************
+/**
+ * @see CActive
+ */
+void CASSrvAlarmTimer::RunL()
+	{
+	// Cache this, so that notifications can queue the next alarm with the
+	// timer.
+	const TAlarmId justExpiredAlarmId = iNextDueAlarmId;
+
+	// Set this back to KNullAlarmId indicating that we don't (yet) have another
+	// alarm to notify about. We must do this just in case there aren't any more
+	// suitable alarms in the queue, otherwise we will always indicate that we
+	// are notifying about an (already) notified alarm.
+	iNextDueAlarmId = KNullAlarmId;
+
+	// check that Internalize hasn't invalidated the head alarm
+	if (!iInvalidatedByInternalize && (iNextDueAlarmId == KNullAlarmId))
+		{
+	const TInt errorStatus = iStatus.Int();
+	switch(errorStatus)
+		{
+	// Time changed - RChangeNotifier for time changes only used if no alarm is set.
+	case KErrAbort:
+		NotifyEvent(MASSrvAlarmTimerObserver::EAlarmTimerEventTimeOrDateChanged, KNullAlarmId);
+		break;
+	
+	// Ignore times set in the past
+	case KErrUnderflow:
+	case KErrNone:
+			if (!iInvalidatedByInternalize)
+		NotifyEvent(MASSrvAlarmTimerObserver::EAlarmTimerEventAlarmExpired, justExpiredAlarmId);
+		break;
+
+	// No timer reset if the date is close to the overflow range. 
+	// This means that agenda will not get notified of time changes
+	case KErrOverflow:
+
+	// Fall through to error handler
+	default:
+		HandleTimerError(justExpiredAlarmId, errorStatus);
+		NotifyEvent(MASSrvAlarmTimerObserver::EAlarmTimerEventTimingError, justExpiredAlarmId);
+		break;
+		}
+
+	// Notify change
+	ServerData().AnyEventManager().MASAnyEventManagerObserverNotifyChanges(EAlarmChangeEventTimerExpired, justExpiredAlarmId);
+		}
+	}
+
+
+//*************************************************************************************
+/**
+ * @see CActive
+ */
+void CASSrvAlarmTimer::Cancel()
+	{
+	CTimer::Cancel();
+	//
+	iNextDueAlarmId = KNullAlarmId;
+	iOriginalExpiryTimeForAlarm = Time::NullTTime();
+	}
+
+
+//*************************************************************************************
+/**
+ * @see CActive
+ */
+TInt CASSrvAlarmTimer::RunError(TInt /*aError*/)
+	{
+	// Ignore erorrs
+	return KErrNone;
+	}
+
+
+//
+//
+//
+
+
+//*************************************************************************************
+/**
+ * Notify observers about the specified event
+ */
+void CASSrvAlarmTimer::NotifyEvent(MASSrvAlarmTimerObserver::TAlarmTimerEvent aEvent, TAlarmId aId)
+	{
+  	for(TInt i=iNotificationList.Count(); i>0; --i)
+   		// Notify observers
+		// TASSrvAlarm::MATimerHandleAlarmExpired() removes itself from the list
+   		iNotificationList[i-1]->MATimerHandleAlarmExpired(aEvent, aId);
+	}
+
+
+//*************************************************************************************
+/**
+ * Handle an error returned in CTimer::iStatus 
+ */
+void CASSrvAlarmTimer::HandleTimerError(TAlarmId aIdOfJustExpiredAlarm, TInt aErrorCode)
+	{
+	CASSrvAlarmQueue& queue = ServerData().Queue();
+	TASSrvAlarm* expiredAlarm = queue.QueueAlarmById(aIdOfJustExpiredAlarm);
+	expiredAlarm->HandleTimerError(aErrorCode);
+	}