pimappservices/calendar/server/src/agsalarm.cpp
changeset 0 f979ecb2b13e
child 11 0f07cd1b5772
equal deleted inserted replaced
-1:000000000000 0:f979ecb2b13e
       
     1 // Copyright (c) 1997-2009 Nokia Corporation and/or its subsidiary(-ies).
       
     2 // All rights reserved.
       
     3 // This component and the accompanying materials are made available
       
     4 // under the terms of "Eclipse Public License v1.0"
       
     5 // which accompanies this distribution, and is available
       
     6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     7 //
       
     8 // Initial Contributors:
       
     9 // Nokia Corporation - initial contribution.
       
    10 //
       
    11 // Contributors:
       
    12 //
       
    13 // Description:
       
    14 //
       
    15 
       
    16 #include "agsalarm.h"
       
    17 #include "agmutil.h"
       
    18 #include "agssortinstance.h"
       
    19 #include "agmentry.h"
       
    20 #include "agsentrymodel.h"
       
    21 #include "agmtlsproxy.h"
       
    22 #include "agmdebug.h"
       
    23 #include "agsfilemanager.h"
       
    24 #include "agsmain.h"
       
    25 #include <centralrepository.h>
       
    26 #include <f32file.h>
       
    27 #include <txtetext.h>
       
    28 #include <tz.h>
       
    29 
       
    30 // This is used when the system time is put forward. All alarms within KSystemTimeChangeMaxDaysOld of 
       
    31 // the new/present time will be queued. If set to 1, and the time is put forward by 7 days then only
       
    32 // alarms within 1 day of the new time will be queued.
       
    33 // (Note: This may need to be changed to a TTime in the future if greater granularity is required.)
       
    34 const TInt KSystemTimeChangeMaxDaysOld = 1;
       
    35 
       
    36 // Maximum number of event alarms to be scheduled when the system time/date changes. Ideally there
       
    37 // shouldn't be a maximum but the existing API FindAndQueueNextFewAlarmsL takes has a
       
    38 // maximum parameter. This should be set to MAX_INT.
       
    39 // (Note: This is the same default value used in the method CAgnAlarm::FindAndQueueNextFewAlarmsL)
       
    40 const TInt KSystemTimeChangeMaxAlarmsQueued = 0xffff;
       
    41 
       
    42 
       
    43 // Central Repository Uid
       
    44 const TUid KUidCentralRepositoryAgenda = { 0x10003a5b };
       
    45  
       
    46 //Used for configuration of whether or not to notify missing alarms
       
    47 const TUint KKeyQueueMissingAlarm = 100;
       
    48 CAgnAlarm::CAgnAlarm(RASCliSession& aRASCliSession)
       
    49     :iAlarmServer(aRASCliSession)
       
    50     {
       
    51     }
       
    52 CAgnAlarm* CAgnAlarm::NewL(CAgnEntryModel* aModel,MAgnAlarmServerTerminationCallBack* aCallBack)
       
    53 /** Allocates and constructs a CAgnAlarm object and makes a connection to 
       
    54 the alarm server.
       
    55 
       
    56 @internalAll
       
    57 @param aModel Pointer to the agenda model which the alarm server connection 
       
    58 is being set up for.
       
    59 @param aCallBack An optional alarm server termination callback provider. May be NULL.
       
    60 @return Pointer to the newly created alarm server interface object. */
       
    61 	{
       
    62 	CAgnAlarm* self = new (ELeave) CAgnAlarm(aModel->AgnServFile().Server().AlarmServer());
       
    63 	
       
    64 	CleanupStack::PushL(self);
       
    65 	self->ConstructL(aModel,aCallBack);
       
    66 	CleanupStack::Pop();
       
    67 	
       
    68 	return (self);
       
    69 	}
       
    70 
       
    71 void CAgnAlarm::DeleteAllAlarms()
       
    72     {
       
    73     // ignore any errors returned from this
       
    74     TParsePtrC parser(iAlarm->AgnServFile().FileName());
       
    75     TFileName fileName = parser.Drive();
       
    76     fileName.Append(parser.NameAndExt());
       
    77     iAlarmServer.AlarmDeleteByCalendarFile(fileName, EFuture|EExpired);
       
    78     }
       
    79 
       
    80 void CAgnAlarm::ConstructL(CAgnEntryModel* aModel,MAgnAlarmServerTerminationCallBack* aCallBack)
       
    81 //
       
    82 // Construct CAgnAlarm
       
    83 //
       
    84 	{
       
    85 	iAlarm = CAgnAlarmActive::NewL(aModel,iAlarmServer);
       
    86 	iChangeObserver = CAgnAlarmServerChangeObserver::NewL(aCallBack,this);
       
    87 
       
    88 	iQueueAlarmsInPast = 1;
       
    89 	CRepository* repository = NULL;
       
    90 	TRAPD(err,  repository = CRepository::NewL(KUidCentralRepositoryAgenda));
       
    91 	if (err == KErrNone && repository)
       
    92 		{
       
    93 		CleanupStack::PushL(repository);
       
    94 		User::LeaveIfError(repository->Get(KKeyQueueMissingAlarm, iQueueAlarmsInPast));
       
    95 		CleanupStack::PopAndDestroy(repository);
       
    96 		}
       
    97 
       
    98 	iAlarm->SetQueueAlarmsInPast(iQueueAlarmsInPast);
       
    99 	}
       
   100 
       
   101 TBool CAgnAlarm::QueueAlarmsInPast()
       
   102 	{
       
   103 	return iQueueAlarmsInPast;
       
   104 	}
       
   105 	
       
   106  CAgnAlarm::~CAgnAlarm()
       
   107 /** Frees all resources owned by the object, prior to its destruction, 
       
   108 and ends the session with the alarm server. 
       
   109 @internalAll
       
   110 */
       
   111 	{
       
   112 	delete iAlarm;
       
   113 	delete iChangeObserver;
       
   114 	}
       
   115 
       
   116 void CAgnAlarm::OrphanAlarm()
       
   117 /** Orphans the session alarm. 
       
   118 
       
   119 This allows the alarm to be serviced after the session is closed. This 
       
   120 function should be called by the user of the model before closing an 
       
   121 agenda file. 
       
   122 
       
   123 @internalAll
       
   124 */
       
   125 	{
       
   126 	iAlarm->OrphanAlarm();
       
   127 	}
       
   128 
       
   129 /** Finds the agenda model's next due alarm. 
       
   130 
       
   131 If a due alarm is found, it is set as the session alarm and queued 
       
   132 with the alarm server. 
       
   133 
       
   134 This function should be called by the client immediately after the CAgnAlarm object is created. 
       
   135 
       
   136 @internalAll
       
   137 @capability WriteUserData
       
   138 */
       
   139 void CAgnAlarm::FindAndQueueNextAlarmL(TBool aSysTimeHasChanged)
       
   140 	{
       
   141 	iAlarm->FindAndQueueNextAlarmL(aSysTimeHasChanged);
       
   142 	}
       
   143 
       
   144 void CAgnAlarm::QueueNextAlarmL(CArrayFixFlat<TAgnSortInstance>*aAlarmedIds)
       
   145 	{
       
   146 	iAlarm->QueueNextAlarmL(aAlarmedIds);
       
   147 	}
       
   148 
       
   149 void CAgnAlarm::FindAndQueueNextFewAlarmsL(const TInt aMaxNumberOfAlarms,const TInt aMaxNumberOfDays)
       
   150 /** Finds the agenda model's next few alarms.
       
   151 
       
   152 If one or more due alarms are found, they are set as session alarms
       
   153 and queued with the alarm server.
       
   154 
       
   155 This method is no longer required by the client because the alarms are now queued
       
   156 by the agenda server once the last session is disconnected to it.
       
   157 
       
   158 @internalAll
       
   159 @param aMaxNumberOfAlarms The maximum number of alarms to be queued. The default 
       
   160 value is 10.
       
   161 @param aMaxNumberOfDays The maximum number of days to be scanned for alarms. 
       
   162 The default value is 31. */
       
   163 	{
       
   164 	iAlarm->FindAndQueueNextFewAlarmsL(aMaxNumberOfAlarms, aMaxNumberOfDays);
       
   165 	}
       
   166 
       
   167 
       
   168 void CAgnAlarm::FindAndQueueNextFewAlarmsL(const TTime& startTime,const TTime& endTime,const TInt aMaxNumberOfAlarms)
       
   169 /* Finds the agenda model's next few alarms.
       
   170 
       
   171 If one or more due alarms are found, they are set as session alarms
       
   172 and queued with the alarm server. 
       
   173 
       
   174 (Note: This method was added to handle system date/time changes. It is not currently exported, however
       
   175  the less general version which takes aMaxNumberOfDays can be used for most purposes.  */
       
   176 	{
       
   177 	iAlarm->FindAndQueueNextFewAlarmsL(startTime,endTime,aMaxNumberOfAlarms);
       
   178 	}
       
   179 
       
   180 /* Gets the TAgnAlarmEntry struct 
       
   181 
       
   182 */
       
   183 const TAgnAlarmEntry* CAgnAlarm::AlarmEntry() const
       
   184 	{
       
   185 	return ( iAlarm->CurrentAlarmEntry() );
       
   186 	}
       
   187 
       
   188 void CAgnAlarm::DeleteEntriesAlarmL(const TAgnEntryId& aEntryId)
       
   189    //Removes all of an entry's alarms from the alarm server.
       
   190    //First, obtain a list of alarms from the alarmserver.
       
   191    //Compare aEntryInstanceId to the TAgnEntryId of the alarm in the queue.
       
   192    //if they are the same then the alarm server is asked to remove the alarm from the queue.
       
   193    	{
       
   194    	RArray<TAlarmId> snoozedAlarmIds;
       
   195    	CleanupClosePushL(snoozedAlarmIds);
       
   196 
       
   197    	iAlarmServer.GetAlarmIdListForCategoryL(KUidAgendaModelAlarmCategory, snoozedAlarmIds);
       
   198   	
       
   199    	if( snoozedAlarmIds.Count() > 0 )
       
   200    		{
       
   201    		for( TInt i = 0; i < snoozedAlarmIds.Count(); ++i )
       
   202 	   		{
       
   203 	   		//get the info for this alarm id
       
   204 	  		TBuf8<sizeof(TAgnAlarmInfo)> alarmInfoBuf;
       
   205 	  		
       
   206 	   		iAlarmServer.GetAlarmData(snoozedAlarmIds[i],alarmInfoBuf);
       
   207 	   		
       
   208 	  		const TAgnAlarmInfo* alarmInfo = reinterpret_cast<const TAgnAlarmInfo*>(alarmInfoBuf.Ptr());
       
   209 	  		if ( alarmInfo->iEntryId == aEntryId )
       
   210 	   			{
       
   211 	   			iAlarmServer.AlarmNotificationCancelAndDequeue(snoozedAlarmIds[i]);
       
   212 	   			}
       
   213 	   		}
       
   214    		}
       
   215    		
       
   216    	CleanupStack::PopAndDestroy(&snoozedAlarmIds);
       
   217    	}
       
   218 #ifdef SYMBIAN_SYSTEM_STATE_MANAGEMENT
       
   219 void CAgnAlarm::DeleteAlarmsAndRequeueSessionAlarmL()
       
   220 	{
       
   221 	iAlarmServer.AlarmDeleteByCategory(KUidAgendaModelAlarmCategory, EFuture|EExpired);
       
   222 	FindAndQueueNextAlarmL(EFalse);
       
   223 	}
       
   224 #endif
       
   225 
       
   226 const CAgnServFile& CAgnAlarm::AgnServFile()
       
   227     {
       
   228     return iAlarm->AgnServFile();
       
   229     }
       
   230 
       
   231 CAgnAlarmServerChangeObserver& CAgnAlarm::ChangeObserver()
       
   232     {
       
   233     return *iChangeObserver;
       
   234     }
       
   235 
       
   236 CAgnAlarmActive* CAgnAlarmActive::NewL(CAgnEntryModel* aModel,RASCliSession& aAlarmServer)
       
   237 //
       
   238 // Create a CAgnAlarmActive object and connect to the alarm server.
       
   239 //
       
   240 	{
       
   241 
       
   242 	CAgnAlarmActive* self=new(ELeave)CAgnAlarmActive(EPriority,aAlarmServer);
       
   243 	
       
   244 	CleanupStack::PushL(self);
       
   245 	self->iAlarmList=new(ELeave)CArrayFixFlat<TAgnAlarmEntry>(EGranularity);
       
   246 	CleanupStack::Pop();
       
   247 	
       
   248 	self->iModel = aModel;
       
   249 	CActiveScheduler::Add(self);
       
   250 	
       
   251 	return ( self );
       
   252 	}
       
   253 
       
   254 CAgnAlarmActive::CAgnAlarmActive(TInt aPriority,RASCliSession& aAlarmServer) 
       
   255 //
       
   256 // Constructor
       
   257 //
       
   258 	: CActive(aPriority), iAlarmServer(aAlarmServer)
       
   259 	{
       
   260 	}
       
   261 
       
   262 
       
   263 void CAgnAlarmActive::OrphanAlarm()
       
   264 //
       
   265 // If there's currently an alarm queued with the alarm server then orphan it.
       
   266 //
       
   267 	{
       
   268 
       
   269 	if ( IsActive() )
       
   270 		{
       
   271 		// Changing the characteristics of the alarm from SessionSpecific to
       
   272 		// non-session specific means the alarm ownership is transferred from
       
   273 		// this session to the alarm server.
       
   274 		//
       
   275 		// Calling this completes the pending request with KErrCancel
       
   276 		TAlarmCharacteristicsFlags alarmCharacteristicsFlags;
       
   277 		iAlarmServer.GetAlarmCharacteristics(iAlarm.Id(), alarmCharacteristicsFlags);
       
   278 		alarmCharacteristicsFlags.Clear(EAlarmCharacteristicsSessionSpecific);
       
   279 		iAlarmServer.SetAlarmCharacteristics(iAlarm.Id(), alarmCharacteristicsFlags);
       
   280 		}
       
   281 		
       
   282 	Cancel();
       
   283 	}
       
   284 
       
   285 
       
   286 CAgnAlarmActive::~CAgnAlarmActive()
       
   287 //
       
   288 // If active then an alarm must be outstanding so create an orphan from it then cancel it.
       
   289 //
       
   290 	{
       
   291 	OrphanAlarm();
       
   292 	delete iAlarmList;
       
   293 	}
       
   294 
       
   295 
       
   296 void CAgnAlarmActive::RunL()
       
   297 //
       
   298 // The alarm server signals iRequestStatus in the following situations:
       
   299 //	- KErrNone when the alarm has expired
       
   300 //	- KErrCancel when the alarm has been canceled
       
   301 //
       
   302 	{
       
   303 
       
   304 	if ( iStatus == KErrNone ) 	// an alarm has expired
       
   305 		{
       
   306 		__ASSERT_DEBUG(iAlarmList->Count()>0, Panic(EAgmNoAlarmsInList)); // the alarm which has just gone off should be in the alarm list
       
   307 
       
   308 		if ( ++iAlarmListIndex < iAlarmList->Count() )
       
   309 		//	if there is another entry with the same alarm time as the one which has just gone off then
       
   310 		//  queue it with the alarm server (its time will now be in the past so it'll go off immediately)
       
   311 			{
       
   312 			const TAgnAlarmEntry& alarmEntry = (*iAlarmList)[iAlarmListIndex];
       
   313 			
       
   314 			// Create and queue an alarm with the Alarm Server
       
   315 			InitialiseAlarmL(iAlarm,alarmEntry,ETrue);
       
   316 			TAgnAlarmInfo alarmInfo;
       
   317 			GetAlarmInfoL(alarmEntry.iInstanceId, alarmInfo);
       
   318 
       
   319 			_DBGLOG_ALARM(AgmDebug::DebugLog("Alarm expired/cancelled. Queueing next alarm:");)
       
   320 			_DBGLOG_ALARM(AgmDebug::DebugLogAlarmL(alarmEntry);)
       
   321 
       
   322 			TPkgCAgnAlarmInfo pkgAgendaRef(alarmInfo);
       
   323 			SetActive();
       
   324 			iAlarmServer.AlarmAddWithNotification(iStatus, iAlarm, pkgAgendaRef);
       
   325 			
       
   326 			if ( iAlarm.Id() == KNullAlarmId )
       
   327 				{
       
   328 				User::Leave(KErrGeneral);
       
   329 				}
       
   330 			}
       
   331 		else
       
   332 			{
       
   333 			FindAndQueueNextAlarmL(EFalse);
       
   334 			}
       
   335 		}
       
   336 	else if( iStatus == KErrCancel )
       
   337 		{
       
   338 		DoCancel();
       
   339 		}
       
   340 	}
       
   341 
       
   342 
       
   343 TBool CAgnAlarmActive::AssignToL(TAgnAlarmEntry& aAlarmEntry, const TAgnSortInstance& aSortInstance)
       
   344 // assign data to the passed alarm entry
       
   345 // Used by QueueNextAlarmL and FindAndQueueNextFewAlarmsL
       
   346 
       
   347 	{
       
   348 	aAlarmEntry.iDueDateTimeLocal = aSortInstance.iStartTimeLocal;
       
   349 	
       
   350 	//to get text in this entry
       
   351 	CAgnEntry* entry = iModel->FetchEntryL(aSortInstance.SimpleEntry().EntryId());
       
   352 	if (! entry)
       
   353 		{
       
   354 		return EFalse;
       
   355 		}
       
   356 		
       
   357 	CleanupStack::PushL(entry);
       
   358 	
       
   359 	if ( entry->RptDef() && !entry->RptDef()->IsAnUnexceptedInstanceL(aSortInstance.InstanceDate()) )
       
   360 		{
       
   361 		// the entry should be not excepted
       
   362 		_DBGLOG_ALARM(AgmDebug::DebugLog("Alarm: PANIC - Invalid Instance: The entry should be not excepted"));
       
   363 		_DBGLOG_ALARM(AgmDebug::DebugLogEntryL(*entry, EDumpEntryAll);)
       
   364 		DBG_PANIC(EAgmErrInvalidInstance);
       
   365 		User::Leave(KErrNotFound);
       
   366 		}
       
   367 
       
   368 	TAgnCalendarTime instanceDate;
       
   369 	if (entry->TimeMode() == MAgnCalendarTimeMode::EFloating)
       
   370 		{
       
   371 		aAlarmEntry.iAlarmTime.SetFloatingL(aSortInstance.InstanceAlarmDateTime());
       
   372 		instanceDate.SetFloatingL(aSortInstance.InstanceDate());
       
   373 		}
       
   374 	else
       
   375 		{
       
   376 		aAlarmEntry.iAlarmTime.SetLocalL(aSortInstance.InstanceAlarmDateTime());
       
   377 		instanceDate.SetLocalL(aSortInstance.InstanceDate());
       
   378 		}
       
   379 	aAlarmEntry.iInstanceId.SetEntryId(entry->EntryId());
       
   380 	aAlarmEntry.iInstanceId.SetDate(instanceDate);
       
   381 	
       
   382 	if ( !entry->SummaryIsLoaded() )
       
   383 		{
       
   384 		entry->SetSummary(iModel->RestoreTextL(entry->SummaryStreamId()));
       
   385 		}
       
   386 
       
   387 	TInt len = entry->Summary().Length();
       
   388 	
       
   389 	if ( len > KMaxAlarmMessageLength )
       
   390 		{
       
   391 		len = KMaxAlarmMessageLength;
       
   392 		}
       
   393 		
       
   394 	aAlarmEntry.iMessage = KNullDesC;
       
   395 	
       
   396 	for ( TInt ii = 0; ii < len; ++ii )
       
   397 		{
       
   398 		TChar ch(entry->Summary()[ii]);
       
   399 		switch (ch)
       
   400 			{
       
   401 		case CEditableText::EParagraphDelimiter :
       
   402 		case CEditableText::ELineBreak :
       
   403 		case CEditableText::EPageBreak :
       
   404 		case CEditableText::ETabCharacter :
       
   405 		case CEditableText::ENonBreakingHyphen :
       
   406 		case CEditableText::EPotentialHyphen :
       
   407 		case CEditableText::ENonBreakingSpace :
       
   408 		case CEditableText::EPictureCharacter :
       
   409 			aAlarmEntry.iMessage.Append(CEditableText::ESpace);
       
   410 			continue;
       
   411 			}
       
   412 		aAlarmEntry.iMessage.Append(ch);
       
   413 		}
       
   414 
       
   415 	aAlarmEntry.iSound = entry->AlarmSoundName();
       
   416 	
       
   417 	CleanupStack::PopAndDestroy(); //entry
       
   418 	
       
   419 	return ETrue;
       
   420 	}
       
   421 
       
   422 /** Find the next due alarm(s) with same time and if at least one exists then queue it with the alarm server
       
   423 @capability WriteUserData
       
   424 */
       
   425 void CAgnAlarmActive::FindAndQueueNextAlarmL(TBool aSysTimeHasChanged)
       
   426 	{
       
   427 	TTime now;
       
   428 	now.HomeTime();
       
   429 
       
   430 	// Certainly on WINS an alarm may expire a few seconds before it is due, the
       
   431 	// following ensures that the same alarm isn't queued twice
       
   432 	// If system time has been changed, we don't want to increase "now" to make sure
       
   433 	// that the alarm whoes due time same as "now" is found.
       
   434 	const TInt KAlarmCount = iAlarmList->Count();
       
   435 	if ( KAlarmCount && !aSysTimeHasChanged)
       
   436 		{
       
   437 		TTime lastAlarm = (*iAlarmList)[KAlarmCount-1].iAlarmTime.LocalL();
       
   438 		
       
   439 		if ( now < lastAlarm )
       
   440 			{
       
   441 			now += TTimeIntervalSeconds(1);
       
   442 			}
       
   443 		}
       
   444 
       
   445 	iAlarmList->Reset();
       
   446 	
       
   447 	CArrayFixFlat<TAgnSortInstance>* alarmedIds = new (ELeave) CArrayFixFlat<TAgnSortInstance>(2);
       
   448 	CleanupStack::PushL(alarmedIds);
       
   449 
       
   450 	iModel->NextAlarmForServerL(now, *alarmedIds);
       
   451 	QueueNextAlarmL(alarmedIds);
       
   452 	
       
   453 	CleanupStack::PopAndDestroy(alarmedIds);
       
   454 	}
       
   455 
       
   456 void CAgnAlarmActive::QueueNextAlarmL(CArrayFixFlat<TAgnSortInstance>* aAlarmedIds)
       
   457 // This function is only called by the server side since we want the alarm is queued in only server side
       
   458 	{
       
   459 	TTime now;
       
   460 	now.HomeTime();
       
   461 
       
   462 	// Certainly on WINS an alarm may expire a few seconds before it is due, the
       
   463 	// following ensures that the same alarm isn't queued twice
       
   464 	const TInt KAlarmCount = iAlarmList->Count();
       
   465 	if ( KAlarmCount )
       
   466 		{
       
   467 		TTime lastAlarm = (*iAlarmList)[KAlarmCount-1].iAlarmTime.LocalL();
       
   468 		
       
   469 		if ( now < lastAlarm )
       
   470 			{
       
   471 			now += TTimeIntervalSeconds(1);
       
   472 			}			
       
   473 		}
       
   474 
       
   475 	iAlarmList->Reset();
       
   476 	
       
   477 	for ( TInt ii = aAlarmedIds->Count() - 1; ii >= 0; --ii )
       
   478 		{
       
   479 		//create an alarmEntry
       
   480 		TAgnAlarmEntry alarmEntry;
       
   481 		
       
   482 		if (AssignToL(alarmEntry, (*aAlarmedIds)[ii]))
       
   483 			{
       
   484 			TKeyArrayFix key(_FOFF(TAgnAlarmEntry, iInstanceId), ECmpNormal8, sizeof(TAgnInstanceId));
       
   485 			iAlarmList->InsertIsqAllowDuplicatesL(alarmEntry, key);		
       
   486 			}
       
   487 		}
       
   488 
       
   489 	Cancel();
       
   490 	
       
   491 	if ( iAlarmList->Count() )
       
   492 	// Queue the first alarm in the alarm list
       
   493 		{
       
   494 		__ASSERT_DEBUG(IsAdded(), Panic(EAgmAgnAlarmActiveNotAdded));
       
   495 		iAlarmListIndex = 0;
       
   496 		const TAgnAlarmEntry& alarmEntry = (*iAlarmList)[0];
       
   497 
       
   498 		InitialiseAlarmL(iAlarm, alarmEntry, ETrue);
       
   499 
       
   500 		TAgnAlarmInfo alarmInfo;
       
   501 		GetAlarmInfoL(alarmEntry.iInstanceId, alarmInfo);
       
   502 
       
   503 		_DBGLOG_ALARM(AgmDebug::DebugLog("Queueing first alarm on list:");)
       
   504 		_DBGLOG_ALARM(AgmDebug::DebugLogAlarmL(alarmEntry);)
       
   505 
       
   506 		TPkgCAgnAlarmInfo pkgAgendaRef(alarmInfo);
       
   507 		SetActive();
       
   508 		iAlarmServer.AlarmAddWithNotification(iStatus, iAlarm, pkgAgendaRef);
       
   509 		
       
   510 		if ( iAlarm.Id() == KNullAlarmId )
       
   511 			{
       
   512 			User::Leave(KErrGeneral);
       
   513 			}
       
   514 		}
       
   515 	}
       
   516 
       
   517 void CAgnAlarmActive::InitialiseAlarmL(TASShdAlarm& aAlarm, const TAgnAlarmEntry& aEntryAlarm, TBool aIsSessionAlarm) 
       
   518 	{
       
   519 	aAlarm.Reset();
       
   520 	aAlarm.Message() = aEntryAlarm.iMessage;
       
   521 	aAlarm.Category() = KUidAgendaModelAlarmCategory;
       
   522 	aAlarm.SoundName() = aEntryAlarm.iSound;
       
   523 	aAlarm.DayOrTimed() = aEntryAlarm.iSessionType;
       
   524 
       
   525 	if (aEntryAlarm.iAlarmTime.TimeMode() == MAgnCalendarTimeMode::EFloating)
       
   526 		{
       
   527 		aAlarm.NextDueTime() = aEntryAlarm.iAlarmTime.LocalL();
       
   528 		}
       
   529 	else
       
   530 		{
       
   531 		aAlarm.SetUtcNextDueTime(aEntryAlarm.iAlarmTime.UtcL());
       
   532 		}
       
   533 
       
   534 	if ( aIsSessionAlarm )
       
   535 		{
       
   536 		aAlarm.Characteristics().Set(EAlarmCharacteristicsSessionSpecific);
       
   537 		}
       
   538 
       
   539 	if (!iQueueAlarmsInPast)
       
   540 		{
       
   541 		aAlarm.Characteristics().Set(EAlarmCharacteristicsDeQueueIfDueTimeInPast);
       
   542 		}
       
   543 	}
       
   544 
       
   545 void CAgnAlarmActive::FindAndQueueNextFewAlarmsL(const TInt aMaxNumberOfAlarms,const TInt aMaxNumberOfDays)
       
   546 	{
       
   547 	TTime now;
       
   548 	now.HomeTime();
       
   549 	
       
   550 	FindAndQueueNextFewAlarmsL(now,now+TTimeIntervalDays(aMaxNumberOfDays),aMaxNumberOfAlarms); 
       
   551 	}
       
   552 
       
   553 void CAgnAlarmActive::FindAndQueueNextFewAlarmsL(const TTime& aStartTime, const TTime& aEndTime, const TInt aMaxNumberOfAlarms)
       
   554 //
       
   555 // Find the next due alarm(s) and if at least one exists then queue it with the alarm server
       
   556 //
       
   557 	{
       
   558 	TTime currTime = aStartTime;
       
   559 
       
   560 	CArrayFixFlat<TAgnAlarmEntry>* alarmList = new (ELeave) CArrayFixFlat<TAgnAlarmEntry>(10);
       
   561 	CleanupStack::PushL(alarmList);
       
   562 	
       
   563 	// Certainly on WINS an alarm may expire a few seconds before it is due, the
       
   564 	// following ensures that the same alarm isn't queued twice
       
   565 	if ( iAlarmList->Count() )
       
   566 		{
       
   567 		TTime lastAlarm = ((*iAlarmList)[0]).iAlarmTime.LocalL();
       
   568 		
       
   569 		if ( currTime < lastAlarm )
       
   570 			{
       
   571 			currTime += TTimeIntervalSeconds(1);
       
   572 			}			
       
   573 		}
       
   574 
       
   575 	CArrayFixFlat<TAgnSortInstance>* alarmedIds = new(ELeave) CArrayFixFlat<TAgnSortInstance>(10);
       
   576 	CleanupStack::PushL(alarmedIds);
       
   577 
       
   578 	iModel->NextFewAlarmsForServerL(currTime, aEndTime, *alarmedIds, aMaxNumberOfAlarms);
       
   579 	
       
   580 	const TInt KAlarmedIdsCount = alarmedIds->Count();
       
   581 	
       
   582 	for ( TInt ii = 0; ii < KAlarmedIdsCount; ++ii )
       
   583 		{
       
   584 		//create an alarmEntry
       
   585 		TAgnAlarmEntry alarmEntry;
       
   586 		
       
   587 		if (AssignToL(alarmEntry, (*alarmedIds)[ii]))
       
   588 			{
       
   589 			TKeyArrayFix key(_FOFF(TAgnAlarmEntry, iAlarmTime), ECmpTInt64);//order by alarm time
       
   590 			alarmList->InsertIsqAllowDuplicatesL(alarmEntry, key);
       
   591 			}
       
   592 		}
       
   593 		
       
   594 	CleanupStack::PopAndDestroy(alarmedIds);
       
   595 
       
   596 	Cancel();
       
   597 
       
   598 	if(iStatus != KErrLocked)
       
   599 		{
       
   600 		TASShdAlarm alarm;
       
   601 		const TInt KCount = alarmList->Count();
       
   602 		for( TInt ii = 0; ii < KCount; ++ii )
       
   603 			{
       
   604 			const TAgnAlarmEntry& alarmEntry = (*alarmList)[ii];
       
   605 
       
   606 			InitialiseAlarmL(alarm, alarmEntry, EFalse);
       
   607 			
       
   608 			TAgnAlarmInfo alarmInfo;
       
   609 			GetAlarmInfoL(alarmEntry.iInstanceId, alarmInfo);
       
   610 			TPkgCAgnAlarmInfo pkgAgendaRef(alarmInfo);
       
   611 
       
   612 			_DBGLOG_ALARM(	AgmDebug::DebugLog("Queueing first alarm on list:");)
       
   613 			_DBGLOG_ALARM(AgmDebug::DebugLogAlarmL(alarmEntry);)
       
   614 
       
   615 			iAlarmServer.AlarmAdd(alarm, pkgAgendaRef);
       
   616 			}
       
   617 		}
       
   618 		
       
   619 	CleanupStack::PopAndDestroy(alarmList);
       
   620 	}
       
   621 
       
   622 // Get the TAgnAlarmEntry struct for the current entry or return NULL if not available
       
   623 const TAgnAlarmEntry* CAgnAlarmActive::CurrentAlarmEntry() const
       
   624 	{
       
   625 	if ( iAlarmList&&iAlarmList->Count() > 0 )
       
   626 		{
       
   627 		return ( &((*iAlarmList)[iAlarmListIndex]) );
       
   628 		}
       
   629 				
       
   630 	return ( NULL );
       
   631 	}
       
   632  
       
   633 void CAgnAlarmActive::DoCancel()
       
   634 //
       
   635 // Cancel the alarm
       
   636 //
       
   637 // DoCancel only gets called from Cancel(). It will only be called if the object is active - which means that
       
   638 // there should therefore be an alarm to cancel.
       
   639 	{
       
   640 	const TAlarmId KId = iAlarm.Id();
       
   641 	if ( KId != KNullAlarmId )
       
   642 		{
       
   643 		iAlarmServer.AlarmNotificationCancelAndDequeue(KId); // resulting signal is picked up in Cancel()
       
   644 		}
       
   645 	}
       
   646 
       
   647 void CAgnAlarmActive::GetAlarmInfoL(const TAgnInstanceId& aInstanceId, TAgnAlarmInfo& aAlarmInfo) const
       
   648 	{
       
   649 	aAlarmInfo.iEntryId = aInstanceId;
       
   650 	
       
   651 	aAlarmInfo.iTimeMode = aInstanceId.Date().TimeMode();
       
   652 	
       
   653 	if(aAlarmInfo.iTimeMode==MAgnCalendarTimeMode::EFloating)
       
   654 		{
       
   655 		aAlarmInfo.iInstanceTime = aInstanceId.Date().LocalL();
       
   656 		}
       
   657 	else
       
   658 		{
       
   659 		aAlarmInfo.iInstanceTime = aInstanceId.Date().UtcL();
       
   660 		}
       
   661 
       
   662 	aAlarmInfo.iAlarmCategory = KUidAgendaModelAlarmCategory;
       
   663  
       
   664  	//Remove path from the filename, leave only Drive:Filename.ext
       
   665 	TParsePtrC parser(iModel->FileName());
       
   666 	aAlarmInfo.iFileName = parser.Drive();
       
   667 	aAlarmInfo.iFileName.Append(parser.NameAndExt());
       
   668 
       
   669 	CAgnEntry* entry = iModel->FetchEntryL(aInstanceId);
       
   670 	__ASSERT_ALWAYS(entry, User::Leave(KErrNotFound)); // alarm has expired for entry which cannot be found or has been deleted
       
   671 
       
   672 	CleanupStack::PushL(entry);
       
   673 	
       
   674 	aAlarmInfo.iAgnUniqueId = entry->LocalUid();
       
   675 	
       
   676 	if(aInstanceId.Date().TimeMode()==MAgnCalendarTimeMode::EFloating)
       
   677 		{
       
   678 		aAlarmInfo.iRecurrenceId = entry->RecurrenceId().LocalL();
       
   679 		}
       
   680 	else
       
   681 		{
       
   682 		aAlarmInfo.iRecurrenceId = entry->RecurrenceId().UtcL();
       
   683 		}
       
   684 	aAlarmInfo.iGlobalIdentifier = iModel->GetEntryGuidL(*entry);
       
   685 	CleanupStack::PopAndDestroy(entry);
       
   686 	}
       
   687 	
       
   688 void CAgnAlarmActive::SetQueueAlarmsInPast(TBool aQueueAlarmsInPast)
       
   689 	{
       
   690 	iQueueAlarmsInPast = aQueueAlarmsInPast;	
       
   691 	}
       
   692 
       
   693 const CAgnServFile& CAgnAlarmActive::AgnServFile()
       
   694     {
       
   695     return iModel->AgnServFile();
       
   696     }
       
   697 
       
   698 CAgnAlarmServerChangeObserver* CAgnAlarmServerChangeObserver::NewL(MAgnAlarmServerTerminationCallBack* aCallBack, CAgnAlarm* aOwningObject)
       
   699 //
       
   700 // Create a CAgnAlarmServerChangeObserver object
       
   701 //
       
   702 	{
       
   703 	CAgnAlarmServerChangeObserver* self = new(ELeave) CAgnAlarmServerChangeObserver(aCallBack, aOwningObject);
       
   704 	CleanupStack::PushL(self);
       
   705 	self->ConstructL();
       
   706 	CleanupStack::Pop(self);
       
   707 	return (self);
       
   708 	}
       
   709 
       
   710 CAgnAlarmServerChangeObserver::CAgnAlarmServerChangeObserver(MAgnAlarmServerTerminationCallBack* aCallBack, CAgnAlarm* aOwningObject)
       
   711 //
       
   712 // C'tor
       
   713 //
       
   714 	:iCallBack(aCallBack), iOwningObject(aOwningObject)
       
   715 	{
       
   716 	}
       
   717 	
       
   718 void CAgnAlarmServerChangeObserver::ConstructL()
       
   719 	{
       
   720 	iTimeZoneConvertor = CAgnTlsProxy::CreateL(CAgnTlsProxy::TAgnTlsTzRulesType_Server);
       
   721 	}
       
   722 
       
   723 
       
   724 CAgnAlarmServerChangeObserver::~CAgnAlarmServerChangeObserver()
       
   725 //
       
   726 // D'tor
       
   727 //
       
   728 	{
       
   729 	delete iSystemId;
       
   730 	CAgnTlsProxy::Release(iTimeZoneConvertor);
       
   731 	}
       
   732 
       
   733 /**
       
   734 Filter notifications from the alarm server, if KErrServerTerminated is received then
       
   735 call the callback function
       
   736 */
       
   737 void CAgnAlarmServerChangeObserver::HandleAlarmServerChangeL(TInt aChange)
       
   738 	{
       
   739 
       
   740 	if ( aChange == KErrServerTerminated )
       
   741 		{
       
   742 		if ( iCallBack )
       
   743 			{
       
   744 			iCallBack->AlarmServerTerminated();
       
   745 			}			
       
   746 		else if ( iUserRequestStatus )
       
   747 			{
       
   748 			User::RequestComplete(iUserRequestStatus,KErrServerTerminated);
       
   749 			iUserRequestStatus = NULL;
       
   750 			}
       
   751 		}
       
   752 	else // iStatus != KErrServerTerminated
       
   753 		{
       
   754 		if (aChange == EAlarmChangeEventSystemDateTimeChanged)
       
   755 			{
       
   756 			CTzId* newSysTime=iTimeZoneConvertor->TzServer().GetTimeZoneIdL();
       
   757 			if( iSystemId && (*iSystemId==*newSysTime) )//when locale changes, we will receive notification twice but we only act once.
       
   758 				{
       
   759 				delete newSysTime;
       
   760 				}
       
   761 			else
       
   762 				{
       
   763 				delete iSystemId;
       
   764 				iSystemId=newSysTime;
       
   765 				}
       
   766 				
       
   767 			const TAgnAlarmEntry* alarmEntry = iOwningObject->AlarmEntry();
       
   768 			TTime queuedEntryTime;
       
   769 
       
   770 			// INC045770: Alarms less than X days old should fire when the system time is put forward.
       
   771 			if ( alarmEntry && iOwningObject->QueueAlarmsInPast())
       
   772 				{
       
   773 				// Get the time of the currently scheduled alarm (serves as the old system time for our purposes.)
       
   774 				queuedEntryTime=alarmEntry->iAlarmTime.LocalL();
       
   775 				// sub 1st sec: FindAndQueue* adds 1 second to prevent scheduling an alarm twice.
       
   776 				// sub 2nd sec: Our sess alarm that has been cancelled needs to be aged 1 sec to get queued.
       
   777 				queuedEntryTime-=TTimeIntervalSeconds(2);  
       
   778 		
       
   779 				TTime now;
       
   780 				now.HomeTime();
       
   781 				TTimeIntervalDays delta = now.DaysFrom(queuedEntryTime);
       
   782 
       
   783 				// clock set forward X days or more after the session alarm; schedule all alarms within X days of the curr time.
       
   784 				if (delta >= TTimeIntervalDays(KSystemTimeChangeMaxDaysOld))
       
   785 					iOwningObject->FindAndQueueNextFewAlarmsL(now-TTimeIntervalDays(KSystemTimeChangeMaxDaysOld),now,KSystemTimeChangeMaxAlarmsQueued);
       
   786 				// clock set forward less than X days after sesion alarm, schedule all alarms between session alarm time and curr time
       
   787 				else if (delta >= TTimeIntervalDays(0))
       
   788 					iOwningObject->FindAndQueueNextFewAlarmsL(queuedEntryTime,now,KSystemTimeChangeMaxAlarmsQueued);				
       
   789 				// else system time has been put backwards (normal code path can handle this case)
       
   790 				}
       
   791 			// *always* schedule a session alarm when the time changes
       
   792 			iOwningObject->FindAndQueueNextAlarmL(ETrue);			
       
   793 			}
       
   794 		else if (aChange == EAlarmChangeEventRestoreCompleted ||
       
   795                     aChange == EAlarmChangeEventRestoreFailed)
       
   796 		    {
       
   797 		    // The restore has finished so try to find and queue the next few alarms
       
   798 		    if (!iOwningObject->AgnServFile().IsLocked()
       
   799                     && !iOwningObject->AgnServFile().IsBackupRestoreLock())
       
   800                 {
       
   801                 iOwningObject->FindAndQueueNextAlarmL(EFalse);
       
   802                 }		   
       
   803 		    }
       
   804 		}
       
   805 	}
       
   806