phonebookengines/contactsmodel/cntsrv/src/CCntStateMachine.cpp
branchRCL_3
changeset 63 f4a778e096c2
child 64 c1e8ba0c2b16
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/phonebookengines/contactsmodel/cntsrv/src/CCntStateMachine.cpp	Wed Sep 01 12:29:52 2010 +0100
@@ -0,0 +1,2764 @@
+// Copyright (c) 2006-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:
+//
+
+/**
+ @file
+ @internalTechnology
+ @released 
+*/
+
+#include "CCntStateMachine.h"
+#include "CCntRequest.h"
+#include "CCntDbManager.h"
+#include "CCntBackupRestoreAgent.h"
+#include "persistencelayer.h"
+#include "CntSpeedDials.h"
+#include <cntfldst.h> 		// for ccontacttextfield
+#include <cntfield.h> 		// for ccontacttextfield
+#include "CNTSTD.H"   		// for Panic codes
+
+// Event related headers
+#include <cntdbobs.h> 	    // for ccontactdbobserver
+#include "CCntServer.h"     // for KCntNullConnectionId.
+#include "CCntEventQueue.h" // for KMaxNumberOfEventsInEventQueue, KCntEventGranularity
+#include "CCntLogger.h"  
+
+// Require SQL header for leave code checking
+#include <sqldb.h>
+	
+/** 
+CState - Base state destructor, default implementataion
+*/
+CState::~CState() 
+	{}
+
+/**
+ CState constructor. 
+ The main purpose of the CState class is to define the state transition table.
+ The CState class implements the most common state behaviour for each request object, 
+ in the overridden AcceptRequestL method.
+ Note there is no NewL method on the CState class. It is not meant to be instantiated.
+ 
+ @param aStateMachine The statemachine object that is used for state transitions
+ @param aPersistenceLayer The persistence layer that allows provides database access
+ 
+*/	
+CState::CState(CCntStateMachine& aStateMachine, CPersistenceLayer&	aPersistenceLayer)
+:iStateMachine(aStateMachine), iPersistenceLayer(aPersistenceLayer)
+	{}
+
+/**
+ TransactionStartLC is called from many derived states. The session that started the
+ transaction is remembered in order to only allow that session perform database operations
+ during the transaction. 
+ Transaction rollback is pushed onto the clean up stack in case of a leave should occur before the
+ transaction is committed. If the transaction can not be committed in full, then none of
+ the transaction should be committed.
+ 
+ @param aSessionId The unique ID of session that is moving the state machine into a transaction state.
+*/
+void CState::TransactionStartLC(TUint aSessionId)
+	{
+	iCurrentTransactionSessionId = aSessionId;
+	iPersistenceLayer.FactoryL().GetCollectorL().Reset();
+	iPersistenceLayer.TransactionManager().StartTransactionL();
+	CleanupStack::PushL(TCleanupItem(CState::CleanupTransactionRollback, this));
+	}
+
+/** 
+ Rollback a transaction after an leave has occured.
+ None of the transaction is committed to the database
+ 
+ @param aState The state in which the leave occured
+*/
+void CState::CleanupTransactionRollback(TAny* aState)
+  	{
+  	TRAP_IGNORE(static_cast<CState*>(aState)->RollbackTransAndRecoverL(EFalse));
+  	}
+
+/**
+ Commit the current transaction.
+*/
+void CState::TransactionCommitLP()
+	{
+	iPersistenceLayer.TransactionManager().CommitCurrentTransactionL(iCurrentTransactionSessionId);
+	iCurrentTransactionSessionId = 0;
+	CleanupStack::Pop(); // CleanupTransactionRollback
+	}
+
+/**
+ Clean up the transaction after a leave occurs. 
+*/
+void CState::RollbackTransAndRecoverL(const TBool aNotification)
+	{
+  	// Some operation has left before a commit could be called.
+  	iPersistenceLayer.TransactionManager().RollbackCurrentTransactionL(iCurrentTransactionSessionId);
+	iCurrentTransactionSessionId = 0;
+	
+	iPersistenceLayer.ContactsFileL().CloseTablesL(!aNotification);
+	iPersistenceLayer.ContactsFileL().OpenTablesL(!aNotification);
+	iStateMachine.SetCurrentStateL(iStateMachine.StateWritable());			
+	}
+
+/* Note: The following methods implement default 
+   AcceptRequestL behaviour for all states derived 
+   from Parent Class CState
+*/
+
+/* 
+ Default behaviour: The file has already been opened 
+ so complete KErrNone.
+ 
+ @param aRequest Open database request object
+ @return TAccept EProcessed - finished processing request
+*/ 
+TAccept CState::AcceptRequestL(CReqAsyncOpen* aRequest)
+	{
+	aRequest->Complete();
+	return EProcessed;
+	}
+
+/**
+ Default behaviour is to defer the request
+
+ @param aRequest Update contact item request object
+ @return EDeferred if the request is not processed, EProcessed if the request is
+         completed with timeout error.
+ @see CState::DeferRequest 
+*/
+TAccept CState::AcceptRequestL(CReqUpdateCnt* aRequest)
+	{
+	return DeferRequest(aRequest);
+	}
+
+/**
+ Default behaviour is to defer the request
+
+ @param aRequest Commit contact item request object
+ @return EDeferred if the request is not processed, EProcessed if the request is
+         completed with timeout error.
+ @see CState::DeferRequest 
+*/
+TAccept CState::AcceptRequestL(CReqCommitCnt* aRequest)
+	{
+	return DeferRequest(aRequest);
+	}
+	
+
+/** 
+ Default behaviour is to allow read access on the database	
+ Open is the same as read but with locking - does not change the database	
+
+ @param aRequest Open contact item request object
+ @return EProcessed if the request is completed or one of the values returned by 
+         CState::DeferRequest if the database is currently locked.
+ @see CState::DeferRequest 
+*/
+TAccept CState::AcceptRequestL(CReqOpenCnt* aRequest)
+	{
+	if (iStateMachine.TransactionLockL().LockLX(aRequest->SessionId(), aRequest->CntItemId()) == KErrInUse)
+		{
+		// The contact item has been locked by another session
+		aRequest->SetTimeOutError(KErrInUse);
+		return DeferRequest(aRequest);
+		}
+
+	CContactItem* cntItem = iPersistenceLayer.PersistenceBroker().ReadLC(aRequest->CntItemId(), aRequest->ItemViewDef(), EPlAllInfo, aRequest->SessionId(), ETrue);
+	aRequest->CompleteL(*cntItem);
+	CleanupStack::PopAndDestroy(cntItem); 
+	CleanupStack::Pop(); // we do not destroy it since that would trigger the leave mechanism and unlock the record
+	return EProcessed;
+	}
+
+/**
+ Default behaviour is to allow read access on the database.
+ Read Contact does not change the database
+ 
+ @param aRequest Read contact item request object
+ @return TAccept EProcessed - finished processing request
+*/ 
+TAccept CState::AcceptRequestL(CReqReadCnt* aRequest)
+	{
+	CContactItem* cntItem = iPersistenceLayer.PersistenceBroker().ReadLC(aRequest->CntItemId(), aRequest->ItemViewDef(), EPlAllInfo, aRequest->SessionId());
+	aRequest->CompleteL(*cntItem);
+	CleanupStack::PopAndDestroy(cntItem);
+	return EProcessed;		
+	}		
+
+/**
+ Default behaviour is to defer the request
+
+ @param aRequest Delete contact item request object
+ @return EDeferred if the request is not processed, EProcessed if the request is
+         completed with timeout error.
+ @see CState::DeferRequest 
+*/
+TAccept CState::AcceptRequestL(CReqDeleteCnt* aRequest)
+	{
+	return DeferRequest(aRequest);	
+	}
+	
+/**
+ Default behaviour is to close the contact item by removing the locking 
+ The lock was added during from an Open Contact Request
+ If the contact id is supplied, explicity unlock that contact item, otherwise
+ unlock the last contact item locked by the session
+
+ @param aRequest Close contact item request object
+ @return TAccept EProcessed - finished processing request
+*/  
+TAccept CState::AcceptRequestL(CReqCloseCnt* aRequest)
+	{
+	if (aRequest->CntItemId() > KNullContactId)
+		{
+		aRequest->Complete(iStateMachine.TransactionLockL().UnLockL(aRequest->SessionId(), aRequest->CntItemId()));	
+		}
+	else
+		{
+		iStateMachine.TransactionLockL().UnlockLastLockedContactL(aRequest->SessionId());
+		aRequest->Complete(KErrNone);
+		}
+	return EProcessed;		
+	}
+
+/**
+ Default behaviour is to always allow a session to unlock any contact 
+ items that remain locked for that session before the session is closed
+
+ @param aRequest Unlock all contact items request object  
+ @return TAccept EProcessed - finished processing request
+*/  
+TAccept CState::AcceptRequestL(CReqInternalSessionUnlock* aRequest)
+	{
+	iStateMachine.TransactionLockL().UnLockAllL(aRequest->SessionId());
+	aRequest->Complete(KErrNone);
+	return EProcessed;		
+	}
+
+/**
+ Default behaviour is to defer the request
+
+ @param Create contact item request object
+ @return EDeferred if the request is not processed, EProcessed if the request is
+         completed with timeout error.
+ @see CState::DeferRequest 
+*/
+TAccept CState::AcceptRequestL(CReqCreateCnt* aRequest)
+	{
+	return DeferRequest(aRequest);	
+	}
+
+/**
+ The default behaviour for Cancelling an Asyncronous Open database command
+ is to accept the file has been opened.
+ 
+ @param aRequest Cancel database open request object 
+ @return TAccept EProcessed - finished processing request
+*/ 
+TAccept CState::AcceptRequestL(CReqCancelAsyncOpen* aRequest)
+	{
+	aRequest->Complete();
+	return EProcessed;	
+	}
+
+
+/**
+ Default behaviour is to defer the request
+   
+ @param aRequest Close contact database tables request object
+ @return EDeferred if the request is not processed, EProcessed if the request is
+         completed with timeout error.
+ @see CState::DeferRequest 
+*/
+TAccept CState::AcceptRequestL(CReqCloseTables* aRequest)
+  	{
+ 	return DeferRequest(aRequest);	
+  	}
+
+/**
+ Default behaviour is to defer the request
+
+ @param aRequest ReOpen contact database tables request object
+ @return EDeferred if the request is not processed, EProcessed if the request is
+         completed with timeout error.
+ @see CState::DeferRequest 
+*/
+TAccept CState::AcceptRequestL(CReqReOpen* aRequest)
+	{
+	return DeferRequest(aRequest);	
+	}
+
+/**
+ Default behaviour is to defer the request
+
+ @param aRequest Begin transaction request object
+ @return EDeferred if the request is not processed, EProcessed if the request is
+         completed with timeout error.
+ @see CState::DeferRequest 
+*/
+TAccept CState::AcceptRequestL(CReqDbBeginTrans* aRequest)
+	{
+	return DeferRequest(aRequest);	
+	}
+
+/**
+ Default behaviour is to defer the request
+
+ @param aRequest Commit transaction request object
+ @return EDeferred if the request is not processed, EProcessed if the request is
+         completed with timeout error.
+ @see CState::DeferRequest 
+*/
+TAccept CState::AcceptRequestL(CReqDbCommitTrans* aRequest)
+	{
+	return DeferRequest(aRequest);	
+	}
+
+/**
+ Default behaviour is to defer the request
+
+ @param aRequest Rollback transaction request object
+ @return EDeferred if the request is not processed, EProcessed if the request is
+         completed with timeout error.
+ @see CState::DeferRequest 
+ */
+TAccept CState::AcceptRequestL(CReqDbRollbackTrans* aRequest)
+	{
+	return DeferRequest(aRequest);	
+	}
+
+/**
+ Default behaviour is to defer the request
+
+ @param aRequest Notification of database backup/restore request object
+ @return EDeferred if the request is not processed, EProcessed if the request is
+         completed with timeout error.
+ @see CState::DeferRequest 
+*/
+TAccept CState::AcceptRequestL(CReqBackupRestoreBegin* aRequest)
+	{
+	return DeferRequest(aRequest);	
+	}
+
+/**
+ Default behaviour is to complete the request
+
+ @param aRequest Notification that a backup/restore has finished request object
+ @return TAccept EProcessed - finished processing request
+*/
+TAccept CState::AcceptRequestL(CReqBackupRestoreEnd* aRequest)
+   	{
+  	// In most cases no backup/restore will be in progress so by default
+  	// complete this request.
+  	aRequest->Complete();
+  	return EProcessed;
+  	}
+  
+/**
+ Default behaviour is set an internal low disk flag to true
+ 
+ @param aRequest Notification of low disk space request object
+ @return TAccept EProcessed - finished processing request
+*/  
+TAccept CState::AcceptRequestL(CReqDiskSpaceLow* aRequest)
+  	{
+  	iStateMachine.SetLowDisk(ETrue);
+  	aRequest->Complete();
+  	return EProcessed;
+  	}
+
+/**
+ Default behaviour is to set an internal low disk flag to false
+
+ @param aRequest Notification disk space is normal request object
+ @return TAccept EProcessed - finished processing request
+*/
+TAccept CState::AcceptRequestL(CReqDiskSpaceNormal* aRequest)
+  	{
+  	iStateMachine.SetLowDisk(EFalse);
+  	aRequest->Complete();
+  	return EProcessed;
+  	}
+
+/**
+ Default behaviour is set an internal async activity flag to true
+ 
+ @param aRequest Notification of async activity request object
+ @return TAccept EProcessed - finished processing request
+*/  
+TAccept CState::AcceptRequestL(CReqAsyncActivity* aRequest)
+  	{
+  	iStateMachine.SetAsyncActivity(ETrue);
+  	aRequest->Complete();
+  	return EProcessed;
+  	}
+
+/**
+ Default behaviour is to set an internal async activity flag to false
+
+ @param aRequest Notification of no async activity request object
+ @return TAccept EProcessed - finished processing request
+*/
+TAccept CState::AcceptRequestL(CReqNoAsyncActivity* aRequest)
+  	{
+  	iStateMachine.SetAsyncActivity(EFalse);
+  	aRequest->Complete();
+  	return EProcessed;
+  	}
+
+/**
+ Default behaviour is to defer the request
+
+ @param aRequest Speed dial request object
+ @return EDeferred if the request is not processed, EProcessed if the request is
+         completed with timeout error.
+ @see CState::DeferRequest 
+*/
+TAccept CState::AcceptRequestL(CReqSetSpeedDial* aRequest)
+	{
+	return DeferRequest(aRequest);	
+	}
+
+/**
+ Default behaviour is to defer the request
+
+ @param aRequest Own card request object
+ @return EDeferred if the request is not processed, EProcessed if the request is
+         completed with timeout error.
+ @see CState::DeferRequest 
+*/
+TAccept CState::AcceptRequestL(CReqSetOwnCard* aRequest)
+	{
+	return DeferRequest(aRequest);	
+	}
+
+
+/**
+ Don't do anything with the event here. Simply propagate the event to the dbmanager
+ Any state that wishes to handle the event implements it's own overwritten 
+ HandleDatabaseEventL method. The only state
+ that actually implements this is the Transaction State.
+ 
+ @param aEvent Database event generated in the Persistence Layer
+*/ 
+void CState::HandleDatabaseEventL(TContactDbObserverEvent aEvent)
+	{
+	iStateMachine.DbManager().HandleDatabaseEventL(aEvent);
+	}
+
+/**
+ Sets a timeout error code on the request. This error code is retrieved from the 
+ derived state class via a call to TimeOutErrorCode() 
+ if the derived state class does not implement a overridden TimeOutErrorCode(), 
+ the CState::TimeOutErrorCode() is used.
+ 
+ Calls CState::DeferRequest to determine if the request should be deferred or not
+ depending on the request's timer status.
+
+ @param aRequest The request on which the timeout error is set.
+ @return EDeferred if the request is not processed, EProcessed if the request is
+         completed with timeout error.
+ @see CState::DeferRequest 
+*/
+TAccept CState::DeferWithTimeOutError(CCntRequest* aRequest)
+	{
+	// use the time out error code specified with the current state
+	aRequest->SetTimeOutError(TimeOutErrorCode());
+
+    return DeferRequest(aRequest);
+	}
+
+/**
+ Determines if an un-processed request should be completed with timeout error or if it 
+ should be re-tried again later, depending on the current status of the request's timer.  
+
+ @param aRequest The request failed to be processed by the current state
+ @return EDeferred - if the request's timer has not expired, the request can be processed again at the 
+                     next opportunity.
+         EProcessed - if the request's timer has expired and is completed with timeout 
+                      error.
+*/	
+TAccept CState::DeferRequest(CCntRequest* aRequest)
+    {
+	// request still cannot be processed, check if the timer on the request
+	// has expired yet
+	if (aRequest->TimeOut() <= 0)
+        {
+        // timer expired, request should be completed with timeout error
+    	aRequest->Complete(aRequest->TimeOutError());
+    	return EProcessed;
+        }
+
+    // timer still valid, as request cannot be processed now, signal to re-try again later
+    return EDeferred;	
+    }
+
+/** 
+ Returns the default timeout - KErrNotReady
+*/
+TInt CState::TimeOutErrorCode()  	
+	{
+	return KErrNotReady;
+	}
+
+
+/** 
+ CStateClosed Class NewL factory constructor
+ @see CState constructor
+*/
+CStateClosed* CStateClosed::NewL(CCntStateMachine& aStateMachine, CPersistenceLayer& aPersistenceLayer)
+	{
+	CStateClosed* stateClosed = new (ELeave) CStateClosed(aStateMachine, aPersistenceLayer);
+	return stateClosed;
+	}
+
+/** 
+ CStateClosed Class constructor
+ @see CState constructor
+*/
+CStateClosed::CStateClosed(CCntStateMachine& aStateMachine, CPersistenceLayer& aPersistenceLayer)
+:CState(aStateMachine, aPersistenceLayer)
+	{}
+
+/** 
+ CStateClosed Class destructor
+ @see CState destructor
+*/
+CStateClosed::~CStateClosed() {}
+
+
+/**
+ Process an open request in the closed state.
+ Note on the State design pattern:
+ Unlike the common state design pattern, most of the work is done in
+ the opening state and not in the closed state before the state transition.
+ The reason for this is re-use of code. 
+ 
+ @param aRequest Open database request object
+ @return TAccept EProcessed if finished processing request
+ 		 	     EDeferred if the request was not processed 
+*/		
+TAccept CStateClosed::AcceptRequestL(CReqAsyncOpen* aRequest)
+	{
+	iStateMachine.SetCurrentStateL(iStateMachine.StateOpening());
+	return iStateMachine.CurrentState().AcceptRequestL(aRequest);
+	}
+
+/**
+ We can only process re-open tables requests in the CStateTablesClosed
+ which means that this request must be preceeded by both a CReqAsyncOpen &
+ a CReqCloseTables. The correct behaviour is to defer the request with
+ the default CStateClosed timeout error.
+ 
+ @param aRequest ReOpen database tables request object 
+ @return EDeferred if the request is not processed, EProcessed if the request is
+         completed with timeout error.
+ @see CState::DeferWithTimeOutError
+*/
+
+TAccept CStateClosed::AcceptRequestL(CReqReOpen* aRequest)
+	{
+	return DeferWithTimeOutError(aRequest);
+	}
+
+
+/** 
+ Overridden read-only operations from base class: can't read from the database
+ while in the Closed state so defer these requests.
+ 
+ @param aRequest Read contact item request object  
+ @return EDeferred if the request is not processed, EProcessed if the request is
+         completed with timeout error.
+ @see CState::DeferWithTimeOutError
+*/
+TAccept CStateClosed::AcceptRequestL(CReqReadCnt* aRequest)
+	{
+	return DeferWithTimeOutError(aRequest);
+	}
+
+/**
+ Defer Update requests with the default error for CStateClosed.
+ Note: This default error code is taken from the original
+ contacts model.
+ 
+ @param aRequest Update contact item request object  
+ @return EDeferred if the request is not processed, EProcessed if the request is
+         completed with timeout error.
+ @see CState::DeferWithTimeOutError
+*/ 
+TAccept CStateClosed::AcceptRequestL(CReqUpdateCnt* aRequest)
+	{
+	return DeferWithTimeOutError(aRequest);
+	}
+
+/** 
+ Defer Commit requests
+ The default error for CStateClosed has been taken from the original 
+ contacts model
+
+ @param aRequest Commit contact item request object  
+ @return EDeferred if the request is not processed, EProcessed if the request is
+         completed with timeout error.
+ @see CState::DeferWithTimeOutError
+*/
+TAccept CStateClosed::AcceptRequestL(CReqCommitCnt* aRequest)
+	{
+	return DeferWithTimeOutError(aRequest);
+	}
+
+/** 
+ Defer Delete requests
+ The default error for CStateClosed has been taken from the original 
+ contacts model
+
+ @param aRequest Delete contact item request object  
+ @return EDeferred if the request is not processed, EProcessed if the request is
+         completed with timeout error.
+ @see CState::DeferWithTimeOutError
+*/
+TAccept CStateClosed::AcceptRequestL(CReqDeleteCnt* aRequest)
+	{
+	return DeferWithTimeOutError(aRequest);
+	}
+
+/** 
+ Defer Create requests
+ The default error for CStateClosed has been taken from the original 
+ contacts model
+
+ @param aRequest Create contact item request object  
+ @return EDeferred if the request is not processed, EProcessed if the request is
+         completed with timeout error.
+ @see CState::DeferWithTimeOutError
+*/
+TAccept CStateClosed::AcceptRequestL(CReqCreateCnt* aRequest)
+	{
+	return DeferWithTimeOutError(aRequest);
+	}
+
+/** 
+ Defer Open requests
+ The default error for CStateClosed has been taken from the original 
+ contacts model
+ The default behaviour of the parent class CStateis to process an open request, the Closed State	
+ defers this request
+
+ @param aRequest Commit contact item request object  
+ @return EDeferred if the request is not processed, EProcessed if the request is
+         completed with timeout error.
+ @see CState::DeferWithTimeOutError
+*/
+TAccept CStateClosed::AcceptRequestL(CReqOpenCnt* aRequest)
+	{
+	return DeferWithTimeOutError(aRequest);
+	}
+
+/**
+ Defer the Close tables request
+ The tables are closed but so is the file. 
+ To close the tables the file must be open
+ 	
+ @param aRequest Close database tables request object  
+ @return EDeferred if the request is not processed, EProcessed if the request is
+         completed with timeout error.
+ @see CState::DeferWithTimeOutError
+*/
+TAccept CStateClosed::AcceptRequestL(CReqCloseTables* aRequest)
+  	{
+  	return DeferWithTimeOutError(aRequest);
+  	}
+  
+/**
+ Defer begin transaction requests  
+ The default behaviour of the parent class CState is to process a begin transaction request, 
+ the Closed State defers this request
+
+ @param aRequest Begin transaction request object  
+ @return EDeferred if the request is not processed, EProcessed if the request is
+         completed with timeout error.
+ @see CState::DeferWithTimeOutError
+*/
+TAccept CStateClosed::AcceptRequestL(CReqDbBeginTrans* aRequest)
+	{
+	return DeferWithTimeOutError(aRequest);
+	}
+
+/**
+ Defer commit transaction requests  
+ The default behaviour of the parent class CState is to process a commit transaction request, 
+ the Closed State defers this request
+
+ @param aRequest Commit transaction request object  
+ @return EDeferred if the request is not processed, EProcessed if the request is
+         completed with timeout error.
+ @see CState::DeferWithTimeOutError
+*/
+TAccept CStateClosed::AcceptRequestL(CReqDbCommitTrans* aRequest)
+	{
+	return DeferWithTimeOutError(aRequest);
+	}
+
+/**
+ Defer rollback transaction requests  
+ The default behaviour of the parent class CState is to process a rollback transaction request, 
+ the Closed State defers this request
+
+ @param aRequest Rollback transaction request object  
+ @return EDeferred if the request is not processed, EProcessed if the request is
+         completed with timeout error.
+ @see CState::DeferWithTimeOutError
+*/
+TAccept CStateClosed::AcceptRequestL(CReqDbRollbackTrans*  aRequest)
+	{
+	return DeferWithTimeOutError(aRequest);
+	}
+
+/**
+ Defer set speed dial requests  
+ The default behaviour of the parent class CState is to process a set speed dial request, 
+ the Closed State defers this request
+
+ @param aRequest Set speed dial request object  
+ @return EDeferred if the request is not processed, EProcessed if the request is
+         completed with timeout error.
+ @see CState::DeferWithTimeOutError
+*/
+TAccept CStateClosed::AcceptRequestL(CReqSetSpeedDial* aRequest)
+	{
+	return DeferWithTimeOutError(aRequest);
+	}
+
+/**
+ Defer set own card requests  
+ The default behaviour of the parent class CState is to process a set own card request, 
+ the Closed State defers this request
+
+ @param aRequest Set own card request object  
+ @return EDeferred if the request is not processed, EProcessed if the request is
+         completed with timeout error.
+ @see CState::DeferWithTimeOutError
+*/	
+TAccept CStateClosed::AcceptRequestL(CReqSetOwnCard* aRequest)
+	{
+	return DeferWithTimeOutError(aRequest);
+	}
+
+
+/**
+ Backup/restore begin notification requests  
+
+ @param aRequest backup/restore begin notification request object  
+ @return TAccept EProcessed
+*/	
+TAccept CStateClosed::AcceptRequestL(CReqBackupRestoreBegin* aRequest)
+  	{
+  	// Backup/restore can take place from this state without doing anything so
+  	// simply complete request.
+  	aRequest->Complete();
+  	return EProcessed;
+  	}
+
+
+/** 
+ CStateTablesClosed Class NewL factory constructor
+ @see CState constructor
+*/  	
+CStateTablesClosed* CStateTablesClosed::NewL(CCntStateMachine& aStateMachine, CPersistenceLayer& aPersistenceLayer)
+  	{
+  	CStateTablesClosed* stateTablesClosed = new (ELeave) CStateTablesClosed(aStateMachine, aPersistenceLayer);
+  	return stateTablesClosed;
+  	}
+  
+/** 
+ CStateTablesClosed Class destructor
+ @see CState Destructor
+*/  	
+CStateTablesClosed::~CStateTablesClosed()
+  	{
+  	}
+  
+/** 
+ CStateTablesClosed Class constructor
+ @see CState constructor
+*/  	
+CStateTablesClosed::CStateTablesClosed(CCntStateMachine& aStateMachine, CPersistenceLayer& aPersistenceLayer) 
+  :CStateClosed(aStateMachine, aPersistenceLayer)
+  	{
+  	}
+  
+  
+/**
+ Accept re-open tables requests  
+ The tables are closed. A re-open request will result in re-opening the tables.
+   
+  @param aRequest re-open tables request object  
+  @return TAccept EProcessed - the request was processed 
+*/
+TAccept CStateTablesClosed::AcceptRequestL(CReqReOpen* aRequest)
+  	{
+  	iPersistenceLayer.ContactsFileL().OpenTablesL(ETrue);
+  	// We can only ever come into this state from the writable state
+  	// as the database must have been opened
+  	iStateMachine.SetCurrentStateL(iStateMachine.StateWritable());
+  	aRequest->Complete();
+  	return EProcessed;		
+  	}
+  
+/**
+  Accept Open database requests  
+  The tables are closed. An async open request will result in re-opening the
+  tables.
+    
+  @param aRequest async open request object  
+  @return TAccept EProcessed if finished processing request
+*/	
+TAccept CStateTablesClosed::AcceptRequestL(CReqAsyncOpen* aRequest)
+  	{	
+  	iPersistenceLayer.ContactsFileL().OpenTablesL(ETrue);
+  	// We can only ever come into this state from the writable state
+  	// as the database must have been opened
+  	iStateMachine.SetCurrentStateL(iStateMachine.StateWritable());
+  	aRequest->Complete();
+  	return EProcessed;
+  	}
+
+/** 
+ CStateOpening Class NewL factory constructor
+ @see CState constructor
+*/ 
+CStateOpening* CStateOpening::NewL(CCntStateMachine& aStateMachine, CPersistenceLayer& aPersistenceLayer)
+	{
+	CStateOpening* stateOpening = new (ELeave) CStateOpening(aStateMachine, aPersistenceLayer);
+	CleanupStack::PushL(stateOpening);
+	stateOpening->ConstructL();
+	CleanupStack::Pop(stateOpening);
+	return stateOpening;
+	}
+
+/** 
+ CStateOpening ConstructL, gets and holds an instance of the persisitnce layer contact file interface
+ and creates and ActiveLoop object which facilitates long running operations.  
+ @see CState constructor
+*/
+void CStateOpening::ConstructL()
+	{
+	iCntFile = &(iPersistenceLayer.ContactsFileL());
+	iActive = CActiveLoop::NewL();
+	}
+/** 
+ CStateOpening constructor
+ @see CState constructor
+*/
+CStateOpening::CStateOpening(CCntStateMachine& aStateMachine, CPersistenceLayer& aPersistenceLayer)
+:CState(aStateMachine, aPersistenceLayer)
+	{
+	}
+
+/** 
+ CStateOpening Class destructor
+ @see CState destructor
+*/	
+CStateOpening::~CStateOpening()
+	{
+	delete iActive;
+	delete iFileName;
+	iOpenReqsStore.ResetAndDestroy();
+	}
+
+/**
+ Accept Open database requests  
+ Opens the database file and tables.
+  
+ @param aRequest Open database request object  
+ @return EOwnershipPassed 
+ @see CStateOpening::OpenDatabaseFileL
+*/
+TAccept CStateOpening::AcceptRequestL(CReqAsyncOpen* aRequest)
+	{
+	SetFileNameL(aRequest->FileName()); 
+	return OpenDatabaseFileL(aRequest);	
+	}
+
+/**
+ Private member function that starts the database opening process.  
+ This method may be called from several AcceptRequestL methods.
+ 
+ @param aRequest Open database request object  
+ @return EOwnershipPassed to indicate ownership of the open database request has 
+         been transferred
+*/
+TAccept CStateOpening::OpenDatabaseFileL(CCntRequest* aRequest, TBool aNotify)
+	{
+	// Add this request to the store where it will be completed on completion of 
+	// the open operaion
+	//
+	// Note that after the request is successfully transferred to the Request 
+	// Store, no leave can occur until the caller is notified that ownership
+	// of the request has been transferred.
+	iOpenReqsStore.AppendL(aRequest);
+
+	iNotify = aNotify;
+
+	// If we are not already processing an open request, start opening the file.
+	if (!iActive->IsActive())
+		{
+		InitialStep();
+		}
+
+	return EOwnershipPassed;
+	}
+
+/**
+ Accept the Reopen tables request as the tables are being opened anyway
+
+ @param aRequest Open database request object  
+ @return EOwnershipPassed 
+ @see CStateOpening::OpenDatabaseFileL
+*/
+TAccept CStateOpening::AcceptRequestL(CReqReOpen* aRequest)
+	{
+	// Open the database and notify (ETrue) all session of the recovery 
+	return OpenDatabaseFileL(aRequest, ETrue);	
+	}
+
+/** 
+ Explicit cancel of an asyncronous Open Database request
+
+ @param aRequest Open database request object  
+ @return TAccept EProcessed 
+*/
+TAccept CStateOpening::AcceptRequestL(CReqCancelAsyncOpen* aRequest)
+	{
+	TInt max = iOpenReqsStore.Count();
+	for (TInt ii = 0; ii < max; ++ii)
+		{
+		if (aRequest->SessionId() == iOpenReqsStore[ii]->SessionId())
+			{
+			// If we only have one concurrent open request, cancel the open
+			// operation
+			if (max == 1)
+				{
+				iCntFile->Close();	
+				iActive->Cancel();
+				iStateMachine.SetCurrentStateL(iStateMachine.StateClosed());
+				}
+			
+			iOpenReqsStore[ii]->Complete(KErrCancel);
+			aRequest->Complete();
+			delete iOpenReqsStore[ii];
+			iOpenReqsStore.Remove(ii);
+			return EProcessed;			
+			}
+		}
+	aRequest->Complete(KErrNotFound);
+	return EProcessed;		
+	}
+
+/**
+ Start Opening the database using the CActiveLoop class. Opening a file
+ is a long running operation and is done in steps allowing other active objects
+ processor time between each opening step.
+*/
+void CStateOpening::InitialStep()
+	{
+	//iActive->Register(*this, ETrue);
+	iActive->Register( *this );
+	}
+
+/** 
+Perform the next step in the opening operation 
+@see InitialStep for more details
+*/
+TBool CStateOpening::DoStepL()
+	{
+	if (!iFileName)
+		{
+		User::Leave(KErrGeneral);
+		}
+
+#ifdef __VERBOSE_DEBUG__
+	RDebug::Print(_L("[CNTMODEL] ------------- Open Database Step -------------"));
+#endif
+
+#if defined(__PROFILE_DEBUG__)
+	RDebug::Print(_L("[CNTMODEL] MTD:CStateOpening::DoStepL"));
+#endif 
+		
+    iCntFile->OpenL(*iFileName);		
+    // always go to this code if using SQLite
+	#ifdef __VERBOSE_DEBUG__
+  		RDebug::Print(_L("[CNTMODEL] ------------- Database Opened -------------"));
+	#endif
+		// The file is now open.
+		DoCompletion(KErrNone);	
+		iStateMachine.SetCurrentStateL(iStateMachine.StateWritable());
+		// Check if Backup/Restore Agent indicates backup in progress (i.e. we
+		// are opening database after backup starts).
+		if (iStateMachine.DbManager().BackupRestoreAgent().BackupInProgress())
+			{
+			TContactDbObserverEvent event;
+			event.iType = EContactDbObserverEventBackupBeginning;
+			event.iContactId = 0;
+			event.iConnectionId = 0;
+			// The HandleBackupRestoreEventL() method of the CCntDbManager that
+			// owns this state machine is called to send the appropriate request
+			// to the state machine and to notify any observers.  The request
+			// causes a transition from CStateWritable to CStateBackupRestore.
+			// We need to send this event here since the event has only been
+			// heard by CCntDbManager instances (and any of its observers) that
+			// existed at the time when the backup first started.
+			iStateMachine.DbManager().HandleBackupRestoreEventL(event);
+			}
+		return EFalse;
+	}
+	
+/** 
+ Complete all Opening requests. More than one client (or internal 
+ request) may open a database file simultaneously. 
+ DoCompletion completes all outstanding Open requests.
+ 
+ @param aError The error code used to complete the request.
+ */
+void CStateOpening::DoCompletion(TInt aError)
+	{
+	TInt max = iOpenReqsStore.Count();
+	for (TInt ii = 0; ii < max; ++ii)
+		{
+		iOpenReqsStore[ii]->Complete(aError);
+		}
+	iOpenReqsStore.ResetAndDestroy();
+	}
+
+/**
+ Completes all open requests with an error code	
+
+ @param aError The error code used to complete the request. 
+*/ 
+void CStateOpening::DoError(TInt aError)
+	{
+	
+	DEBUG_PRINT2(__VERBOSE_DEBUG__,_L("[CNTMODEL] ------------- Database Open Error %d -------------"), aError);
+
+	DoCompletion(aError);
+	TRAP_IGNORE(iStateMachine.SetCurrentStateL(iStateMachine.StateClosed() ) );
+	}
+
+/**
+ Sets the name of the file that is being opened. The name is held by
+ the opeing state and re-used when database needs to be closed and 
+ re-opened (for example during recovery)
+ 
+ @param aFileName The name of the file that is being opened
+*/ 
+void CStateOpening::SetFileNameL(const TDes& aFileName)
+	{
+	HBufC* tmpFileName  = HBufC::NewL(aFileName.Length());
+	*tmpFileName = aFileName;
+	delete iFileName;
+	iFileName = tmpFileName;
+	}
+
+/**
+ Once a Backup or Restore has completed we can re-open the database.
+ 
+ @param aRequest Notification of Backup/Restore end request object  
+ @return EOwnershipPassed 
+ @see CStateOpening::OpenDatabaseFileL
+*/ 
+TAccept CStateOpening::AcceptRequestL(CReqBackupRestoreEnd* aRequest) 
+	{
+	return OpenDatabaseFileL(aRequest);
+	}
+
+/** 
+ Overridden read-only operations from base class: can't read from the database
+ while in the Opening state so defer these requests.
+ 
+ @param aRequest Read contact item request object  
+ @return EDeferred if the request is not processed, EProcessed if the request is
+         completed with timeout error.
+ @see CState::DeferRequest 
+*/
+TAccept CStateOpening::AcceptRequestL(CReqReadCnt* aRequest)
+	{
+	return DeferRequest(aRequest); // Uses the requests default timeout error
+	}
+
+// @see CStateOpening::AcceptRequestL(CReqReadCnt* )	
+TAccept CStateOpening::AcceptRequestL(CReqOpenCnt* aRequest)
+	{
+	return DeferRequest(aRequest);
+	}
+
+
+
+// CStateWritable Class implementation //
+
+/** 
+ CStateWritable Class NewL factory constructor
+ @see CState constructor
+*/ 
+CStateWritable* CStateWritable::NewL(CCntStateMachine& aStateMachine, CPersistenceLayer& aPersistenceLayer)
+	{
+	CStateWritable* stateWritable = new (ELeave) CStateWritable(aStateMachine, aPersistenceLayer);
+	return stateWritable;
+	}
+
+/** 
+ CStateWritable Class constructor
+ @see CState constructor
+*/ 
+CStateWritable::CStateWritable(CCntStateMachine& aStateMachine, CPersistenceLayer& aPersistenceLayer)
+:CState(aStateMachine, aPersistenceLayer)
+	{
+	}
+
+/** 
+ CStateWritable Class destructor
+ @see CState destructor
+*/ 
+CStateWritable::~CStateWritable()
+	{
+	}
+
+
+// Derived AcceptRequestL methods - Vistor Pattern
+	
+/**
+ Update a contact item in the database
+
+ @param aRequest Update contact item request object  
+ @return TAccept EProcessed if finished processing request or one of the 
+         values returned by DeferWithTimeOutError
+ @see CState::DeferWithTimeOutError 		 	     
+*/ 	
+TAccept CStateWritable::AcceptRequestL(CReqUpdateCnt* aRequest)
+	{
+  	if (iStateMachine.LowDisk())
+  		{
+  		aRequest->Complete(KErrDiskFull);
+  		return EProcessed;
+  		}
+  	// Check if the contact has been locked 
+  	if (iStateMachine.TransactionLockL().IsLocked(aRequest->Item().Id()))
+  		{
+  		// If the request can not be procesed after the timeout period, it should 
+  		// complete with KErrInUse to assure binary compatibility with the original model
+		return DeferWithTimeOutError(aRequest);
+  		}
+  
+  	TRAPD(updateErr,
+  		{
+		TransactionStartLC(aRequest->SessionId());
+		
+		iPersistenceLayer.PersistenceBroker().SetConnectionId(aRequest->SessionId());
+		iPersistenceLayer.PersistenceBroker().UpdateL(aRequest->Item(), aRequest->SessionId());
+	
+		TransactionCommitLP();
+  		});
+	if (updateErr == KSqlErrGeneral)
+		{
+		// Write operation failed, probably due to view read activity
+		return DeferWithTimeOutError(aRequest);
+		}
+	else if (updateErr != KErrNone)
+		{
+		// Unknown error, propagate the leave to the client
+		User::Leave(updateErr);
+		}
+
+	aRequest->Complete();
+	return EProcessed;		
+	}		
+
+/**
+ Commit an opened contact item to the database  
+ Unlock the contact item so other session can modify it.
+ Starts and commits a local transaction inorder to handle leaves.
+ 
+ @param aRequest Commit contact item request object  
+ @return TAccept EProcessed if finished processing request or one of the 
+         values returned by DeferWithTimeOutError
+ @see CState::DeferWithTimeOutError 		 	     
+*/
+TAccept CStateWritable::AcceptRequestL(CReqCommitCnt* aRequest)
+	{
+  	if (iStateMachine.LowDisk())
+  		{
+  		aRequest->Complete(KErrDiskFull);
+  		return EProcessed;
+  		}
+
+	// Check if the contact has been locked by this session
+  	if (iStateMachine.TransactionLockL().IsLocked(aRequest->SessionId(), aRequest->Item().Id()))
+  		{
+  		// If the request can not be procesed after the timeout period, it should 
+  		// complete with KErrInUse to assure binary compatibility with the original model
+		return DeferWithTimeOutError(aRequest);
+  		}
+	
+  	TRAPD(commitErr,
+  		{
+		TransactionStartLC(aRequest->SessionId());
+	    iPersistenceLayer.PersistenceBroker().SetConnectionId(aRequest->SessionId());
+		iPersistenceLayer.PersistenceBroker().UpdateL(aRequest->Item(), aRequest->SessionId());
+		User::LeaveIfError(iStateMachine.TransactionLockL().UnLockL(aRequest->SessionId(), aRequest->Item().Id()));
+		TransactionCommitLP();
+		});
+  	if (commitErr == KSqlErrGeneral)
+  		{
+  		// Can't commit contact, probably due to idle sorter activity
+  		return DeferWithTimeOutError(aRequest);
+  		}
+  	else
+  		{
+  		User::LeaveIfError(commitErr);
+  		}
+  	
+	aRequest->Complete();
+	return EProcessed;		
+	}
+
+
+/**
+ Delete a contact item from the database. 
+ Starts and commits a local transaction inorder to handle leaves.
+ Checks if the contact item is locked by another session.
+ 
+ @param aRequest Delete contact item request object  
+ @return TAccept EProcessed if finished processing request or one of the 
+         values returned by DeferWithTimeOutError
+ @see CState::DeferWithTimeOutError
+*/
+TAccept CStateWritable::AcceptRequestL(CReqDeleteCnt* aRequest)
+	{
+	// Check if the contact has been locked 
+	if (iStateMachine.TransactionLockL().IsLocked(aRequest->CntItemId()))
+  		{
+  		// If the request can not be procesed after the timeout period, it should 
+  		// complete with KErrInUse - the contact is locked 
+		return DeferWithTimeOutError(aRequest);
+		}	
+	
+	TRAPD(deleteErr,
+		{
+		TransactionStartLC(aRequest->SessionId());
+	
+		iPersistenceLayer.PersistenceBroker().SetConnectionId(aRequest->SessionId());
+		CContactItem* cntItem = iPersistenceLayer.PersistenceBroker().DeleteLC(aRequest->CntItemId(), aRequest->SessionId(), ESendEvent);
+		CleanupStack::PopAndDestroy(cntItem);
+	
+		TransactionCommitLP();
+		});
+	if (deleteErr == KSqlErrGeneral)
+		{
+		// Write operation failed, probably due to view read activity
+		return DeferWithTimeOutError(aRequest);
+		}
+	else if (deleteErr != KErrNone)
+		{
+		// Unknown error, propagate the leave to the client
+		User::Leave(deleteErr);
+		}
+		
+	aRequest->Complete();
+	return EProcessed;		
+	}		
+	
+/**
+ Create a new contact item
+
+ @param aRequest Create contact item request object  
+ @return TAccept EProcessed 
+*/
+TAccept CStateWritable::AcceptRequestL(CReqCreateCnt* aRequest)
+	{
+  	if (iStateMachine.LowDisk())
+  		{
+  		aRequest->Complete(KErrDiskFull);
+  		return EProcessed;
+  		}
+	
+  	TContactItemId cntID(KNullContactId);
+  	TRAPD(createErr,
+  		{
+	  	TransactionStartLC(aRequest->SessionId());
+	    
+		iPersistenceLayer.PersistenceBroker().SetConnectionId(aRequest->SessionId());
+	    cntID = iPersistenceLayer.PersistenceBroker().CreateL(aRequest->Item(), aRequest->SessionId());
+		
+	    TransactionCommitLP();
+  		});
+	if (createErr == KSqlErrGeneral)
+		{
+		// Write operation failed, probably due to view read activity
+		return DeferWithTimeOutError(aRequest);
+		}
+	else if (createErr != KErrNone)
+		{
+		// Unknown error, propagate the leave to the client
+		User::Leave(createErr);
+		}
+	
+	aRequest->Complete(cntID);
+	return EProcessed;		
+	}		
+
+/**
+ Change the current state of the model to CStateTablesClosed. 
+ The database is unavailable.		
+  
+ @param aRequest Close database tables request object  
+ @return TAccept EProcessed if finished processing request
+*/   
+TAccept CStateWritable::AcceptRequestL(CReqCloseTables* aRequest)
+  	{
+  	iPersistenceLayer.ContactsFileL().CloseTablesL(ETrue);	
+  	iStateMachine.SetCurrentStateL(iStateMachine.StateTablesClosed());
+  	aRequest->Complete();
+  	return EProcessed;		
+  	}
+
+
+/** 
+ Do nothing for Cancel - it's too late as the database is already open.
+
+ @param aRequest Cancel open database request object  
+ @return TAccept EProcessed if finished processing request
+*/ 
+TAccept CStateWritable::AcceptRequestL(CReqCancelAsyncOpen* aRequest)
+	{
+	aRequest->Complete();
+	return EProcessed;		
+	}
+
+
+/**
+ Start a database transaction by moving to a transaction state		
+ 
+ @param aRequest Begin transaction request object  
+ @return TAccept EProcessed if finished processing request
+ 		 	     EDeferred if the request was not processed 
+*/ 
+TAccept CStateWritable::AcceptRequestL(CReqDbBeginTrans* aRequest)
+	{
+  	// In the current implementation there are no operations allowed under the
+  	// low disk condition that will reduce the size of the database: this is in
+  	// line with Contacts Model 1 behaviour.  Later, when we allow operations
+  	// that reduce the size of the database, this check should be removed and
+  	// allow the transition to CStateTransaction.
+  	if (iStateMachine.LowDisk())
+  		{
+  		aRequest->Complete(KErrDiskFull);
+  		return EProcessed;
+  		}	
+	
+	iStateMachine.SetCurrentStateL(iStateMachine.StateTransaction());
+	return iStateMachine.CurrentState().AcceptRequestL(aRequest);
+	}
+		
+/**
+ Start a database backup/restore by moving to CStateBackupRestore state.  If any
+ contact items are locked or there is any asynchronous activity using the
+ database then we cannot close the file (and the backup/restore client will not
+ be allowed to backup/restore).
+
+ @param aRequest Notification of backup/restore begin request object  
+ @return TAccept EProcessed if finished processing request
+*/ 
+TAccept CStateWritable::AcceptRequestL(CReqBackupRestoreBegin* aRequest)
+	{
+	if (!iStateMachine.TransactionLockL().AnyLocked() &&
+		!iStateMachine.AsyncActivity())
+		{
+		// First reset collection, since it construct views based on table 
+		// Reset will fail if called after closing tables 
+		iPersistenceLayer.FactoryL().GetCollectorL().Reset(); 
+		// Close the file to allow the backup/restore to take place.	
+		iPersistenceLayer.ContactsFileL().Close();
+		}
+	iStateMachine.SetCurrentStateL(iStateMachine.StateBackupRestore());
+	aRequest->Complete();
+	return EProcessed;
+	}
+
+
+/**
+ Reset the speed dials		
+
+ @param aRequest Reset speed dials request object  
+ @return TAccept EProcessed if finished processing request
+ 		 	     EDeferred if the request was not processed 
+*/ 
+TAccept CStateWritable::AcceptRequestL(CReqSetSpeedDial* aRequest)
+	{
+	if (iStateMachine.LowDisk())
+  		{
+  		aRequest->Complete(KErrDiskFull);
+  		return EProcessed;
+  		}
+	
+	TContactItemId contactId = aRequest->TheContactId();
+	if(contactId == 0)
+		{
+		User::Leave(KErrArgument);
+		}
+
+	// Obtain the contact ID currently associated with the speed dial index.
+	// The phone number is not being used at the moment
+	TSpeedDialPhoneNumber phoneNumberFromSpeedDialTable;
+	TContactItemId OldContactId = aRequest->SpeedDialTable().SpeedDialContactItem(aRequest->SpeedDialIndex(), phoneNumberFromSpeedDialTable);
+	
+	// We should not be able to remove a speed dial from an open item, even if
+	// it has been opened by the same session: use the IsLocked() method which
+	// ignores session ID.
+    TBool isLocked = iStateMachine.TransactionLockL().IsLocked(OldContactId);		
+				
+	// This code resets an entry from the speed dial table, as required when calling either 
+	// CContactDatabase::ResetServerSpeedDialsL() (contactId is KNullContactId) or 
+	// CContactDatabase::RemoveSpeedDialFieldL()  (contactId must be equal to the OldContactId)
+	// If the field index is -1, it indicates that the speed dial entry corresponding 
+	// to the speed dial index passed in the request should be reset.	
+	TBool doResetOldContactItem = ETrue;
+	TBool doResetSpeedDialEntry = aRequest->TheFieldIndex() == -1;			
+	
+	if (doResetSpeedDialEntry)
+		{		
+		if (contactId == KNullContactId || contactId == OldContactId)
+		    {
+		    if (isLocked)
+		        {
+		        User::Leave(KErrInUse);
+		        }			    
+		        
+		    aRequest->IniFileManager().SetSpeedDialIdForPositionL(aRequest->SpeedDialIndex(), KNullContactId, KNullDesC(), aRequest->SessionId(), EFalse);				    
+		    }
+		else
+		    {
+		    doResetOldContactItem = EFalse;
+		    }
+		}
+
+	//Everything, i.e. removal of the old speed dial reference and 
+	//the setting of the new takes place during the same transaction
+	//i.e. start transaction here
+	TransactionStartLC(aRequest->SessionId());
+		{
+		// Check if there is already a contact associated with this speed dial
+		// index.
+		if (OldContactId != KErrNotFound && doResetOldContactItem)
+			{
+			// Fetch the item from the ID, remember to pop it
+			CContactItem* cntItem = iPersistenceLayer.PersistenceBroker().ReadLC(OldContactId, aRequest->ItemViewDef(), EPlAllInfo, aRequest->SessionId());	
+			// Remove speed dial attributes from the contact item field.
+			TUid fieldTypeUid = CCntServerSpeedDialManager::SpeedDialFieldUidFromSpeedDialPosition(aRequest->SpeedDialIndex());
+			TInt fieldIdFound = cntItem->CardFields().Find(fieldTypeUid);
+			if (fieldIdFound != KErrNotFound)
+				{		
+				cntItem->CardFields()[fieldIdFound].RemoveFieldType(fieldTypeUid);
+				cntItem->CardFields()[fieldIdFound].SetSpeedDial(EFalse);
+				}
+			// Update changes to the contact item in the database.
+			iPersistenceLayer.PersistenceBroker().SetConnectionId(aRequest->SessionId());
+			iPersistenceLayer.PersistenceBroker().UpdateL(*cntItem, aRequest->SessionId(), ETrue);
+			CleanupStack::PopAndDestroy(cntItem);
+			}
+		
+		if (!doResetSpeedDialEntry)
+			{
+			// Fetch the contact item containing the phone number to be used as
+			// a speed dial.
+			CContactItem* cntItem = iPersistenceLayer.PersistenceBroker().ReadLC(contactId, aRequest->ItemViewDef(), EPlAllInfo, aRequest->SessionId());	
+			if (cntItem->CardFields().Count() < 1)
+				{
+				User::Leave(KErrUnknown);		
+				}	
+			// Get the field containing the number to be associated with the
+			// speed dial.
+			CContactItemField& speeddialField = cntItem->CardFields()[aRequest->TheFieldIndex()];
+			// Add speed dial attributes to the contact item field.
+			TUid fieldTypeUid = CCntServerSpeedDialManager::SpeedDialFieldUidFromSpeedDialPosition(aRequest->SpeedDialIndex());
+			if (!speeddialField.ContentType().ContainsFieldType(fieldTypeUid))
+				{
+				speeddialField.AddFieldTypeL(fieldTypeUid);
+				}
+			speeddialField.SetUserAddedField(ETrue);
+			speeddialField.SetSpeedDial(ETrue);
+			// Get the phone number from the field.
+			if (speeddialField.StorageType() != KStorageTypeText)
+				{
+				User::Leave(KErrArgument);		
+				}	
+			// Truncate it if its length is > KSpeedDialPhoneLength
+			TInt numLen = Min(speeddialField.TextStorage()->Text().Length(), KSpeedDialPhoneLength);
+			TPtrC phoneNumber(speeddialField.TextStorage()->Text().Mid(0, numLen));
+			// Update changes to the contact item in the database.
+			iPersistenceLayer.PersistenceBroker().SetConnectionId(aRequest->SessionId());
+			// Update the speed dial table.
+			aRequest->IniFileManager().SetSpeedDialIdForPositionL(aRequest->SpeedDialIndex(), contactId, phoneNumber, aRequest->SessionId(), EFalse);
+			iPersistenceLayer.PersistenceBroker().UpdateL(*cntItem, aRequest->SessionId(), ETrue);
+			// Unlock the item.
+			User::LeaveIfError(iStateMachine.TransactionLockL().UnLockL(aRequest->SessionId(), contactId));
+			CleanupStack::PopAndDestroy(cntItem);
+			}
+		}
+	TransactionCommitLP();
+	aRequest->Complete();
+	return EProcessed;		
+	}
+
+
+/**
+ Set own card data	
+
+ @param aRequest Set own card request object  
+ @return TAccept EProcessed if finished processing request
+ 		 	     EDeferred if the request was not processed 
+*/ 		
+TAccept CStateWritable::AcceptRequestL(CReqSetOwnCard* aRequest)
+	{
+	if (iStateMachine.LowDisk())
+  		{
+  		aRequest->Complete(KErrDiskFull);
+  		return EProcessed;
+  		}
+
+	TUid aContactType = aRequest->Item().Type();
+
+	// this should leave with kerrnotsupported if the type doesn't match!!!!
+	if (aContactType==KUidContactGroup || aContactType==KUidContactTemplate || aContactType==KUidContactCardTemplate)
+		{
+		User::Leave(KErrNotSupported);	
+		}
+	// if the requested item is already set as own card then just return
+	if (aRequest->Item().Id()==iPersistenceLayer.ContactProperties().OwnCardIdL())
+		{
+		aRequest->Complete();
+		return EProcessed;	
+		}
+		
+	// if the requested ID is system template or KNUllContactID then leave
+	if (aRequest->Item().Id()==KGoldenTemplateId || aRequest->Item().Id()==KNullContactId)
+		{
+		User::Leave(KErrNotFound);
+		}
+	//Everything, i.e. removal of the old own card reference and 
+	//the setting of the new takes place during the same transaction
+	//i.e. start transaction here
+	TransactionStartLC(aRequest->SessionId());
+		{//reset the old own card to become an ordinary contacts card
+		if (iPersistenceLayer.ContactProperties().OwnCardIdL() != KNullContactId)
+			{
+			// Fetch the old current item from the ID, remember to pop it
+			CContactItem* oldOwnCard = iPersistenceLayer.PersistenceBroker().ReadLC(iPersistenceLayer.ContactProperties().OwnCardIdL(), aRequest->ItemViewDef(), EPlAllInfo, aRequest->SessionId());	
+			iPersistenceLayer.PersistenceBroker().SetConnectionId(aRequest->SessionId());
+			//change the type to KUidContactCard for the old ownCard
+			iPersistenceLayer.PersistenceBroker().ChangeTypeL(oldOwnCard->Id(), KUidContactCard);
+			CleanupStack::PopAndDestroy(oldOwnCard);
+			}			
+		iPersistenceLayer.PersistenceBroker().SetConnectionId(aRequest->SessionId());
+		//Now set the new own card
+		iPersistenceLayer.PersistenceBroker().ChangeTypeL(aRequest->Item().Id(), KUidContactOwnCard);
+		}
+	TransactionCommitLP();
+	aRequest->Complete();
+	return EProcessed;		
+	}		
+
+/** 
+ The error code which deferred requests use after a timeout from the 
+ writable state  this maintians consistency with the original contacts model
+ 
+ @return The most common the timeout error code -KErrInUse- used in the writable state
+*/ 
+TInt CStateWritable::TimeOutErrorCode()
+	{
+	return KErrInUse;		
+	}
+
+
+
+// CStateTransaction Implementation//
+/** 
+ CStateTransaction Class NewL factory constructor
+ Create a transaction state
+ @see CState constructor
+*/ 
+CStateTransaction* CStateTransaction::NewL(CCntStateMachine& aStateMachine, CPersistenceLayer& aPersistenceLayer)
+	{
+	CStateTransaction* stateTransaction = new (ELeave) CStateTransaction(aStateMachine, aPersistenceLayer);
+	CleanupStack::PushL(stateTransaction);
+	stateTransaction->ConstructL();
+	CleanupStack::Pop(stateTransaction);
+	return stateTransaction;
+	}
+
+/**
+ Instantiate the CTransctionTimer object. The transaction state contains a timer
+ because a client may put the server into a transation state and for some unknown 
+ reason my never complete the transaction. Such a senario would result in the server
+ becoming unusable (ie No other client can modify the database until the transaction 
+ is completed or rolled back). If the CStateTransaction class does not hear from the
+ client for a given time, the transaction times out and is rolled back. This is the 
+ responsibility of the CTransactionTimer.
+*/
+void CStateTransaction::ConstructL()
+	{
+	// Pass the transaction state that will timeout.
+	iTimeOut = CTransactionTimer::NewL(*this); 
+	}
+
+/** 
+ CStateTransaction Class constructor
+ @see CState constructor
+*/ 
+CStateTransaction::CStateTransaction(CCntStateMachine& aStateMachine, CPersistenceLayer& aPersistenceLayer)
+: CState(aStateMachine, aPersistenceLayer), iEventQ(KCntEventGranularity)
+	{
+	}
+
+/** 
+ CStateTransaction Class constructor
+ @see CState constructor
+*/ 
+CStateTransaction::~CStateTransaction()
+	{
+	delete iTimeOut;
+	iEventQ.Close();
+	}
+	
+/**	
+ Cancel the transaction - will result in a database rollback
+ implemented in the base CState class
+ This overwritten CancelTransaction is only ever called when the transaction
+ times out. The base class CState::CancelTransactionL() is called when any 
+ state class or persistence layer method leaves
+*/
+void CStateTransaction::CancelTransactionL()
+  	{
+  	CState::RollbackTransAndRecoverL(EFalse); 
+  	iSessionId = 0; // Allow another session enter a transaction state.
+  	}
+
+/**	
+ Get the CStateTransaction default timeout error code.
+ 
+ @return TInt ErrLocked
+*/
+TInt CStateTransaction::TimeOutErrorCode()
+	{
+	return KErrLocked;	
+	}
+
+
+/**
+ Begin a database transaction, start the transaction timeout which rolls-back
+ the transaction if the session dies. Reset the transactions event queue - this
+ event-queue queues all events until the transaction has been committed.
+ 
+ @param aRequest Begin a database transaction
+ @return TAccept EProcessed if finished processing request, or one of the values returned
+                 by CState::DeferRequest 
+ @see CState::DeferRequest 
+*/ 
+TAccept CStateTransaction::AcceptRequestL(CReqDbBeginTrans* aRequest)
+	{
+	
+	#if defined(__PROFILE_DEBUG__)
+		RDebug::Print(_L("[CNTMODEL] MTD: CStateTransaction::AcceptRequestL"));
+	#endif 
+	
+	// Only one session can ever be in a transaction state
+	if (iSessionId == 0)
+		{
+		iSessionId = aRequest->SessionId();
+		iPersistenceLayer.FactoryL().GetCollectorL().Reset();
+		iPersistenceLayer.TransactionManager().StartTransactionL();
+		iTimeOut->Start();
+		aRequest->Complete();
+		// Reset the event queue - although it is also reset when a transaction
+		// is committed or explicitly rolled back, it will now be reset if a rollback occured 
+		// because of a leave. Resetting the queue after commit/explicit rollback free's memory.
+		iEventQ.Reset(); 
+		return EProcessed;	
+		}
+	// This session has already started a transaction 	
+	if (iSessionId == aRequest->SessionId())
+		{
+		aRequest->Complete();
+		return EProcessed;	
+		}
+	// Another session has started a transaction
+	return DeferRequest(aRequest); 
+	}
+
+
+/**
+ Commit the current transaction if started by the same session.
+ Stop the transaction timer.
+ Propogate all events to the observer.
+ Compress the database.
+ 
+ @param aRequest Commit a database transaction
+ @return TAccept EProcessed if the transaction has been committed
+ @leave KErrLocked if a different session had started the transaction
+*/
+TAccept CStateTransaction::AcceptRequestL(CReqDbCommitTrans* aRequest)
+	{
+ 	if (iSessionId == aRequest->SessionId())
+ 		{
+		TRAPD(commitErr, iPersistenceLayer.TransactionManager().CommitCurrentTransactionL(aRequest->SessionId()));
+		if (commitErr == KSqlErrGeneral)
+			{
+			// Operation has probably been blocked due to read lock by view idle sorter.
+			return DeferWithTimeOutError(aRequest);
+			}
+		else
+			{
+			User::LeaveIfError(commitErr);
+			}
+		
+		iTimeOut->Stop(); // Transaction completed - shouldn't timeout
+
+		// The database had been updated. All session should now be notified
+		// of events.
+		PropagateDatabaseEventsL();
+		
+		iStateMachine.SetCurrentStateL(iStateMachine.StateWritable());				
+
+		iSessionId = 0;
+		// Only complete the request after the last leaving method. If 
+		// a leave occurs after the request (message) has been completed, then the method 
+		// CCntSession::ServiceError will try to complete the message a second time
+		// causing a panic. 
+		aRequest->Complete();
+ 		}
+ 	else
+		{
+		StrayRequestL(aRequest); // Only the current session should be able to
+		}						 // send a commit transaction request
+	return EProcessed;		
+	}
+
+
+
+/** 
+ Rollback the current transaction if started by the same session
+ Reset the event queue as no operation has been committed to the database.
+ Don't compress the database as it hasn't changed.
+ Notify the observer that a rollback has occured.
+ 
+ @param aRequest Rollback a database transaction
+ @return TAccept EProcessed if the transaction has been rolled back
+ @leave KErrLocked if a different session had started the transaction
+*/
+TAccept CStateTransaction::AcceptRequestL(CReqDbRollbackTrans* aRequest)
+	{
+ 	if (iSessionId == aRequest->SessionId())
+ 		{
+		iEventQ.Reset(); // Empty the event queue - no operations have been committed
+						 // so sessions should never be notified of the event
+
+ 		iCurrentTransactionSessionId = aRequest->SessionId();
+		CState::RollbackTransAndRecoverL(ETrue);
+		iSessionId = 0;
+
+		// Transaction completed - shouldn't timeout
+		iTimeOut->Stop(); 
+	
+		// Rollback event needs to be propagated.
+		TContactDbObserverEvent event;
+		event.iType			= EContactDbObserverEventRollback;
+		event.iContactId	= KNullContactId;
+		event.iConnectionId = iSessionId;
+		iStateMachine.DbManager().HandleDatabaseEventL(event);
+
+		aRequest->Complete();
+ 		}
+ 	else
+		{
+		StrayRequestL(aRequest); // Only the current session should be able
+		}						 // to rollback a transaction
+	return EProcessed;
+	}
+
+
+/** 
+ A session tried to commit or rollback a transaction that was started
+ by another session. Destroy the request and leave.
+
+ @param aRequest A pointer to a request object scoped to it parent class.
+ @leave KErrLocked A different session had started the transaction
+*/ 
+void CStateTransaction::StrayRequestL(CCntRequest* /* aRequest */)
+	{
+	User::Leave(KErrLocked);	
+	}
+
+
+/**
+ Create a contact item from a the session that started the transaction
+ 
+ @param aRequest The request that will contain the created contact item 
+ @return TAccept EProcessed if finished processing request
+ 		 	     EDeferred if the request was not processed 
+*/
+TAccept CStateTransaction::AcceptRequestL(CReqCreateCnt* aRequest)
+	{
+	#if defined(__PROFILE_DEBUG__)
+		RDebug::Print(_L("[CNTMODEL] MTD: CStateTransaction::AcceptRequestL"));
+	#endif 	
+	
+  	if (iStateMachine.LowDisk())
+  		{
+  		aRequest->Complete(KErrDiskFull);
+  		return EProcessed;
+  		}
+	
+ 	if (iSessionId == aRequest->SessionId())
+ 		{
+ 		TContactItemId itemId(KNullContactId);
+ 		TRAPD(createErr, itemId = iPersistenceLayer.PersistenceBroker().CreateL(aRequest->Item(),  aRequest->SessionId()));
+ 		if (createErr == KSqlErrGeneral)
+ 			{
+ 			// Can't create contact item, probably due to view idle sorter activity
+ 			return DeferWithTimeOutError(aRequest);
+ 			}
+ 		else
+ 			{
+ 			User::LeaveIfError(createErr);
+ 			}
+ 		
+		aRequest->Complete(itemId);
+		iTimeOut->Reset(); // restart the timeout as the client session is still alive
+		return EProcessed;
+		}
+ 	
+ 	// The session that is trying to perform this operation has not started the transaction
+ 	return DeferWithTimeOutError(aRequest);
+	}
+
+/**
+ Read a contact item - always allow read operation from any session 
+ ie the session does not need to have started the transaction to read the contact item
+ 
+ @param aRequest The request that will contain the contact item read from the database
+ @return TAccept EProcessed if finished processing request
+ 		 	     EDeferred if the request was not processed 
+*/ 
+TAccept CStateTransaction::AcceptRequestL(CReqReadCnt* aRequest)
+	{
+	iTimeOut->Reset(); // Reset the timeout, the client is still alive
+	return CState::AcceptRequestL(aRequest);
+	}
+	
+/**
+ Update a contact item from a the session that started the transaction	
+ 
+ @param aRequest The request that contain the contact item that is to be updated in the database
+ @return TAccept EProcessed if finished processing request, or one of the values returned
+                 by CState::DeferRequest 
+ @see CState::DeferRequest 
+*/ 
+TAccept CStateTransaction::AcceptRequestL(CReqUpdateCnt* aRequest)
+	{
+  	if (iStateMachine.LowDisk())
+  		{
+  		aRequest->Complete(KErrDiskFull);
+  		return EProcessed;
+  		}
+
+	// Check if the contact has been locked by any session - including this session
+	// This is for reasons of compatibility with the original model only
+  	if (iStateMachine.TransactionLockL().IsLocked(aRequest->Item().Id()) == EFalse)
+  		{
+  		 if (iSessionId == aRequest->SessionId())
+	 		{
+	 		TRAPD(updateErr, iPersistenceLayer.PersistenceBroker().UpdateL(aRequest->Item(), aRequest->SessionId()));
+			if (updateErr == KSqlErrGeneral)
+				{
+				// Can't update item, probably due to idle sorter activity
+				return DeferWithTimeOutError(aRequest);
+				}
+			else
+				{
+				User::LeaveIfError(updateErr);
+				}
+			
+			aRequest->Complete();
+			iTimeOut->Reset(); 
+			
+			return EProcessed;
+			}
+	 	else
+	 		{
+	 		// The session that is trying to perform this operation has not started the transaction
+	 		return DeferWithTimeOutError(aRequest);
+	 		}
+  		}
+  	else
+  		{
+  		// If the request can not be procesed after the timeout period, it should 
+  		// complete with KErrInUse as the contact is locked
+  		aRequest->SetTimeOutError(KErrInUse);  		
+  		}
+	return DeferRequest(aRequest);	
+	}
+	
+/**
+ Delete a contact item if the delete request is from the same session that started the transaction
+ 
+ @param aRequest The request that contain the contact item ID that is to be deleted from the database
+ @return TAccept EProcessed if finished processing request, or one of the values returned
+                 by CState::DeferRequest 
+ @see CState::DeferRequest 
+*/ 
+TAccept CStateTransaction::AcceptRequestL(CReqDeleteCnt* aRequest)
+	{
+	if (iStateMachine.LowDisk())
+  		{
+  		aRequest->Complete(KErrDiskFull);
+  		return EProcessed;
+  		}	
+	
+	// Check if the contact has been locked by any session - including this session
+  	if (iStateMachine.TransactionLockL().IsLocked(aRequest->CntItemId()) == EFalse)
+  		{
+	 	if (iSessionId == aRequest->SessionId())
+	 		{
+	 		CContactItem* item = NULL; 
+	 		TRAPD(deleteErr, item = iPersistenceLayer.PersistenceBroker().DeleteLC(aRequest->CntItemId(), aRequest->SessionId(), aRequest->NotificationEventAction());CleanupStack::PopAndDestroy(item));
+	 		if (deleteErr == KSqlErrGeneral)
+	 			{
+	 			// Delete failed, probably due to idle sorter activity
+	 			return DeferWithTimeOutError(aRequest);
+	 			}
+	 		else
+	 			{
+	 			User::LeaveIfError(deleteErr);
+	 			}
+	 		
+			aRequest->Complete();
+			iTimeOut->Reset(); 
+
+			return EProcessed;	
+	 		}
+	 	else
+	 		{
+	 		// The session that is trying to perform this operation has not started the transaction
+	 		return DeferWithTimeOutError(aRequest);
+	 		}
+  		}
+  	else
+  		{
+  		// If the request can not be procesed after the timeout period, it should 
+  		// complete with KErrInUse as the contact is locked
+  		aRequest->SetTimeOutError(KErrInUse);  		
+  		}
+
+	return DeferRequest(aRequest);	
+	}
+
+
+/**
+ Commit (write & unlock) a contact item that has been locked to the database
+ The contact item is unlocked.
+ 
+ @param aRequest The request that contain the contact item that is to be written to the database
+ @return TAccept EProcessed if finished processing request
+ 		 	     EDeferred if the request was not processed 
+*/ 
+TAccept CStateTransaction::AcceptRequestL(CReqCommitCnt* aRequest)
+	{
+	if (iStateMachine.LowDisk())
+		{
+		aRequest->Complete(KErrDiskFull);
+		return EProcessed;
+		}
+
+ 	if (iSessionId == aRequest->SessionId())
+ 		{
+ 		User::LeaveIfError(iStateMachine.TransactionLockL().UnLockL(aRequest->SessionId(), aRequest->Item().Id()));
+ 		TRAPD(updateErr, iPersistenceLayer.PersistenceBroker().UpdateL(aRequest->Item(), aRequest->SessionId()));
+ 		if (updateErr == KSqlErrGeneral)
+ 			{
+ 			// Can't update contact, probably due to idle sorter activity
+ 			return DeferWithTimeOutError(aRequest);
+ 			}
+ 		else
+ 			{
+ 			User::LeaveIfError(updateErr);
+ 			}
+ 		
+		aRequest->Complete();
+		iTimeOut->Reset(); 
+		
+		return EProcessed;	
+		}
+ 	else
+ 		{
+ 		// The session that is trying to perform this operation has not started the transaction
+ 		return DeferWithTimeOutError(aRequest);
+ 		}
+	}
+
+/**
+ Open (read and lock) the contact item, returning the opened contact item.
+ The contact item is also locked
+ 
+ @param aRequest The request that will contain the contact item that is to be opened (read and locked) 
+ 				 in the database
+ @return TAccept EProcessed if finished processing request
+ 		 	     EDeferred if the request was not processed 
+*/ 
+TAccept CStateTransaction::AcceptRequestL(CReqOpenCnt* aRequest)
+	{
+ 	if (iSessionId == aRequest->SessionId())
+ 		{
+ 		// As a valid operation has been performed by the session, it is still 
+ 		// alive so the timeout for the transaction state must be reset.
+		iTimeOut->Reset(); 
+		return CState::AcceptRequestL(aRequest);
+ 		}
+  	else
+ 		{
+ 		// The session that is trying to perform this operation has not started the transaction
+ 		return DeferWithTimeOutError(aRequest);	
+ 		}
+	}
+
+/**
+ Close (unlock) the locked contact without commiting the contact item to the database 
+ only if the session also started the transaction
+ 
+ @param  aRequest The request that contain the contact item that is to be closed (unlocked but not updated)
+ @return One of the values returned by DeferWithTimeOutError, or CState::AcceptRequest(CReqCloseCnt*)
+ @see CState::DeferWithTimeOutError
+ @see CState::AcceptRequest(CReqCloseCnt*)
+*/ 
+TAccept CStateTransaction::AcceptRequestL(CReqCloseCnt* aRequest)
+	{
+ 	if (iSessionId == aRequest->SessionId())
+ 		{
+		iTimeOut->Reset(); 
+		return CState::AcceptRequestL(aRequest);
+		}
+ 	else
+ 		{
+ 		// The session that is trying to perform this operation has not started the transaction
+ 		return DeferWithTimeOutError(aRequest);
+ 		}
+	}
+
+/**
+ Hanlde a database event while in the transaction state.
+ The CStateTransaction holds all events, and does not propagate them to 
+ the DbManager, until an explicit commit request is called and the database
+ is written to. All events are not valid until a commit is called as a 
+ transaction rollback would invalidate the events.
+ 
+ @param aEvent The database event that is being propogated.
+*/
+void CStateTransaction::HandleDatabaseEventL(TContactDbObserverEvent aEvent)
+	{
+	
+	DEBUG_PRINT1(__VERBOSE_DEBUG__,_L("[CNTMODEL] Database Event in Transaction"));
+
+	// Do not add a rollback event to the queue. This event will be propagated as 
+	// part of the transaction rollback.
+	if (aEvent.iType == EContactDbObserverEventRollback)
+		{
+		return;
+		}
+	if (iEventQ.Count() <= KMaxNumberOfEventsInEventQueue)
+		{
+		
+		DEBUG_PRINT1(__VERBOSE_DEBUG__,_L("[CNTMODEL] Database Event Added To Q in Transaction"));
+	
+		iEventQ.AppendL(aEvent);	
+		} 
+	// else - do nothing as a EContactDbObserverEventUnknownChanges will be propagated
+	// to all observers	
+	}
+
+
+/** 
+ Propagate events back to the CCntDbManager after a commit transaction request
+*/
+void CStateTransaction::PropagateDatabaseEventsL()
+	{
+	if (iEventQ.Count() >= KMaxNumberOfEventsInEventQueue)
+		{
+		
+		DEBUG_PRINT1(__VERBOSE_DEBUG__,_L("[CNTMODEL] Max Database Events reached in Transaction - Unknown changes event"));
+
+		// Tell the observer to re-sync with the database as there has been too many
+		// operations for which it needs to recieve notification
+		TContactDbObserverEvent unknownChangeEvent;
+		unknownChangeEvent.iType 		 = EContactDbObserverEventUnknownChanges;
+		unknownChangeEvent.iContactId 	 = KNullContactId;
+		unknownChangeEvent.iConnectionId = KCntNullConnectionId;
+		
+		iStateMachine.DbManager().HandleDatabaseEventL(unknownChangeEvent);
+		}
+	else
+		{
+		
+		DEBUG_PRINT1(__VERBOSE_DEBUG__,_L("[CNTMODEL] Database Events being propagated in Transaction"));
+	
+		TInt max = iEventQ.Count();
+		TInt ii  = 0;
+		// Propagate the events from the beginning of the EventQ
+		while(ii != max) 
+			{
+			iStateMachine.DbManager().HandleDatabaseEventL(iEventQ[ii]);	
+			++ii;	
+			}
+		}
+	iEventQ.Reset(); // Empty the queue	
+	}
+
+
+
+// CTransactionTimer Implementation //
+
+/** 
+ CTransactionTimer Class NewL factory constructor
+ Create a transaction timer
+
+ The transaction timer is used to allow a transaction to timeout should
+ the session which put the state machine into a transaction state die
+ unexpectedly. It is derived from CTimer and times out after sixty seconds.
+*/ 
+CTransactionTimer* CTransactionTimer::NewL(CStateTransaction& aTransState)
+	{
+	CTransactionTimer* self = new (ELeave) CTransactionTimer(aTransState);
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop(self);
+	return self;
+	}
+	
+/** 
+ CTransactionTimer Class destructor
+*/	
+CTransactionTimer::~CTransactionTimer()
+	{
+	CTimer::Cancel();
+	}
+	
+/**
+ CTransactionTimer constructor
+*/	
+CTransactionTimer::CTransactionTimer(CStateTransaction& aTransState) 
+				 : CTimer(CActive::EPriorityIdle), iTransState(aTransState)
+	{
+	}
+
+void CTransactionTimer::ConstructL()
+	{
+	CTimer::ConstructL();
+	CActiveScheduler::Add(this);
+	}
+
+
+/** 
+ The Transaction was neither commited nor rolled back 
+ The client may have died - clean up the state machine by rolling back
+*/ 
+void CTransactionTimer::RunL()
+	{
+	iTransState.CancelTransactionL();
+	CTimer::Cancel();
+	}
+
+/**
+ Start the timer. This is done when a the state machine moves 
+ into the transaction state.
+*/
+void CTransactionTimer::Start()
+	{ // wait for 60 seconds
+	CTimer::Cancel();
+	CTimer::After(KSixtySeconds);
+	}
+
+/**
+ Stop the timer. This is done when a the state machine moves 
+ out of the transaction state.
+*/
+void CTransactionTimer::Stop()
+	{
+	CTimer::Cancel();
+	}
+
+/**
+ When a valid operation is performed within the transaction state
+ the timer is reset to sixty seconds, if another transaction operation is not
+ performed within sixty seconds, the transaction should timeout.
+*/
+void CTransactionTimer::Reset()
+	{
+	CTimer::Cancel();
+	CTimer::After(KSixtySeconds);
+	}
+	
+// CTransactionLock Class Implementation //
+/** 
+ CTransactionLock Class NewL factory constructor
+ The CTransactionLock class locks contacts allowing only the locking session to
+ modify the contact item in the database.
+*/
+CTransactionLock* CTransactionLock::NewL(CCntStateMachine& aStateMachine)
+	{
+	CTransactionLock* self = new (ELeave) CTransactionLock(aStateMachine);
+	return self;
+	}
+	
+// TLockData constructor	
+CTransactionLock::TLockData::TLockData(TContactItemId aCntId, const TUint aSessionId):iCntItemId(aCntId), iSessionId(aSessionId)
+	{}
+	
+// --------- Locking Methods -----------
+/** 
+ Locks a contact item by adding its ID to an array of locked contact items IDs.
+ After a session locks the contact item, no other session can modify the locked contact
+ item. 
+ Adds the lock to the clean up stack, indicated by the 'X' in the method name
+ 
+ @param aCntId The ID of contact item to be locked
+ @param aSessionId The Session which is locking the contact Item
+ aContact The contact item to add to the database.
+ @return KErrNone if locking was successful.
+		KErrInUse if the contact item was locked by another session 
+*/
+
+TInt CTransactionLock::LockLX(const TUint aSessionId, const TContactItemId aCntId)
+	{
+	
+	DEBUG_PRINT2(__VERBOSE_DEBUG__,_L("[CNTMODEL] *   Lock item %d"), aCntId);
+	
+	if (IsLocked(aCntId))
+		{
+		return KErrInUse; // A session can only lock a cnt item once.	
+		}
+		
+	TLockData lockData(aCntId, aSessionId);
+	
+	iLockedIds.InsertInSignedKeyOrderL(lockData);
+	
+	CleanupStack::PushL(TCleanupItem(CTransactionLock::CleanupUnlockRecord, this));
+		
+	return KErrNone;	
+	}
+
+	
+/**
+ Unlocks the last locked contact item after a leave
+ 
+ @param aTransLock The CTransactionLock object from which the last locked contact item
+ 				  ID must be removed (unlocked).
+*/
+void CTransactionLock::CleanupUnlockRecord(TAny* aTransLock)
+	{
+	TRAP_IGNORE(static_cast<CTransactionLock*>(aTransLock)->UnlockLastLockedContactL() );
+	}	
+	
+/** 
+ UnLocks a contact item by removing its ID to an array of locked contact items IDs.
+ 
+ @param aCntId The ID of contact item to be unlocked
+ @param aSessionId The Session which is unlocking the contact Item
+ @return KErrNone if locking was successful.
+ 		 KErrAccessDenied if the contact item was not successfuly locked  
+*/	
+TInt CTransactionLock::UnLockL(const TUint aSessionId, const TContactItemId aCntId)
+	{
+	
+	DEBUG_PRINT2(__VERBOSE_DEBUG__,_L("[CNTMODEL] * UnLock item %d"), aCntId);
+	
+	TLockData lockData(aCntId, aSessionId);
+	TInt index = iLockedIds.FindInSignedKeyOrder(lockData);
+	if (index < 0)
+		return KErrAccessDenied;
+	
+	if (index > iLockedIds.Count())
+		{
+		return KErrAccessDenied;	
+		}
+	
+	if (iLockedIds[index].iSessionId == aSessionId)
+		{
+		iLockedIds.Remove(index);
+		ProcessLockedContactsL(); // Process any requests in the Store
+		}
+	return KErrNone;	
+	}
+
+/**
+ Process any requests in the Store - Another session
+ may have been trying to perform an operation on a  
+ locked contact item. This method is called after a contact item
+ has been unlocked to allow an operation on the same contact item
+ to be performed by another session.
+*/
+void CTransactionLock::ProcessLockedContactsL()
+	{
+	if(iStateMachine.ReqStoreL().IsEmpty() == EFalse)
+		{
+		iStateMachine.ReqStoreL().ActivateRequestsL();	
+		}		
+	}
+
+/**
+ Unlocks all the locked contacts for a given sessionid, the request 
+ that calls this method, CReqInternalSessionUnlock, originates in the session destructor
+ 
+ @param aSessionId The session that is being closed.
+*/ 
+void CTransactionLock::UnLockAllL(const TUint aSessionId)
+	{
+	TInt ii	= iLockedIds.Count();
+	while(ii) 
+		{
+		--ii;		
+		if (iLockedIds[ii].iSessionId == aSessionId)
+			{
+			iLockedIds.Remove(ii);
+			}
+		}
+	ProcessLockedContactsL(); // Process any requests in the Store
+	}
+	
+/** 
+ Unlock the last locked contact after an leave has occured	
+ 
+ @param aSessionId The ID of the session that performed the operation in which 
+ 				   the leave occured.
+*/
+void CTransactionLock::UnlockLastLockedContactL(TUint aSessionId)	
+	{
+	if (aSessionId == nsState::KNoSessionId)
+		{
+		// Remove the last Locked Contact regardless of session
+		iLockedIds.Remove(iLockedIds.Count() - 1);
+		ProcessLockedContactsL(); // Process any requests in the Store
+		return;
+		}
+	
+	TInt ii	= iLockedIds.Count();
+	while(ii) 
+		{
+		--ii;		
+		if (iLockedIds[ii].iSessionId == aSessionId)
+			{
+			iLockedIds.Remove(ii);
+			ProcessLockedContactsL(); // Process any requests in the Store
+			return; // Finished
+			}
+		}
+	}
+	
+/** 
+Checks if a contact item is locked by another session.
+
+@param aCntId The ID of contact item to be checked
+@param aSessionId The Session which is checking for a lock
+
+@return True if the contact has been locked.
+		False if the contact has not been locked by another session.
+*/
+TBool CTransactionLock::IsLocked(const TUint aSessionId, const TContactItemId aCntId) const
+	{
+	TInt ii	= iLockedIds.Count();
+	
+	while(ii) 
+		{
+		--ii;		
+		if (iLockedIds[ii].iCntItemId == aCntId)
+			{
+			if (iLockedIds[ii].iSessionId != aSessionId)
+				{
+				return ETrue;	// locked by another session	
+				}
+			return EFalse; // has not been locked by another session	
+			}
+		}
+	return EFalse; // has not been locked by any session
+	}
+
+/**
+ Checks if a contact item is locked by this or another session (any session).
+ Original behaviour of the contacts model was not to allow a session to lock a contact twice.
+
+ @param aCntId The ID of contact item to be checked
+ @return True if the contact has been locked.
+		False if the contact has not been locked.
+*/
+TBool CTransactionLock::IsLocked(const TContactItemId aCntId)const
+	{
+	TInt ii = iLockedIds.Count();
+	
+	while(ii) 
+		{
+		--ii;		
+		if (iLockedIds[ii].iCntItemId == aCntId)
+			{
+			return ETrue;	// locked 
+			}
+		}
+	return EFalse;
+	}
+
+/**
+ Determines if any contacts items are locked by this or another session (any
+ session).
+ @return True if there is a locked contact.
+		False if there is no locked contact.
+*/
+TBool CTransactionLock::AnyLocked() const
+	{
+	return iLockedIds.Count() != 0;
+	}
+
+/** CStateBackupRestore
+	While in this state neither read nor write operations are allowed.  The
+	CStateClosed parent class AcceptRequestL() handles all read and write
+	requests.
+	
+	Methods completes the request with KErrLocked via a call to TimeOutErrorCode() 
+	from the parent CStateClosed class.
+*/
+CStateBackupRestore* CStateBackupRestore::NewL(CCntStateMachine& aStateMachine, CPersistenceLayer& aPersistenceLayer)
+	{
+	CStateBackupRestore* stateBUR = new (ELeave) CStateBackupRestore(aStateMachine, aPersistenceLayer);
+	return stateBUR;
+	}
+	
+	
+CStateBackupRestore::~CStateBackupRestore()
+	{
+	}
+
+
+CStateBackupRestore::CStateBackupRestore(CCntStateMachine& aStateMachine, CPersistenceLayer& aPersistenceLayer)
+:CStateClosed(aStateMachine, aPersistenceLayer)
+	{
+	}
+
+
+// Default AsyncOpen requests behaviour to the base implementation
+TAccept CStateBackupRestore::AcceptRequestL(CReqAsyncOpen* aRequest)
+	{
+	return CState::AcceptRequestL(aRequest);
+	}
+
+
+TAccept CStateBackupRestore::AcceptRequestL(CReqBackupRestoreBegin* aRequest)
+  	{
+  	// A backup/restore is already in progress so don't defer this request -
+  	// simply complete it.
+	aRequest->Complete();
+  	return EProcessed;
+  	}	
+
+
+TAccept CStateBackupRestore::AcceptRequestL(CReqBackupRestoreEnd* aRequest)
+	{
+	// Once Backup/Restore completes we can re-open the database.  Re-accept
+	// request in CStateOpening.	
+	iStateMachine.SetCurrentStateL(iStateMachine.StateOpening());  
+	return iStateMachine.CurrentState().AcceptRequestL(aRequest);
+ 	}
+
+
+/**
+ If there was asynchronous activity then it has now finished so safe to close
+ database.
+ 
+ @param aRequest Notification of end of async activity request object
+ @return TAccept EProcessed - finished processing request
+*/  
+TAccept CStateBackupRestore::AcceptRequestL(CReqNoAsyncActivity* aRequest)
+  	{
+  	if (iStateMachine.AsyncActivity())
+  		{
+  		iStateMachine.SetAsyncActivity(EFalse);
+		// Close the file to allow the backup/restore to take place.	
+		iPersistenceLayer.ContactsFileL().Close();
+		}
+  	aRequest->Complete();
+  	return EProcessed;
+  	}
+
+/** 
+ Returns the default error code - KErrLocked - for the backup restore state 
+*/
+TInt CStateBackupRestore::TimeOutErrorCode()  	
+	{
+	return KErrLocked;
+	}
+
+
+// ======================================		
+// CCntStateMachine Class implementation	
+// The main purpose of the CState object 
+// is to define the state transition table
+	
+CCntStateMachine::~CCntStateMachine()
+	{
+	iStateArray.ResetAndDestroy();
+	delete iReqStore;
+	delete iTransLock;
+	}
+	
+CCntStateMachine* CCntStateMachine::NewL(CPersistenceLayer& aPersistenceLayer, CCntDbManager& aDbManager)
+	{
+	CCntStateMachine* stateMachine = new (ELeave) CCntStateMachine(aDbManager);
+	CleanupStack::PushL(stateMachine);
+	stateMachine->ConstructL(aPersistenceLayer);
+	CleanupStack::Pop(stateMachine);
+	return stateMachine;
+	}
+	
+/** 
+ Create and add all states to the state machine array	
+ 
+ @param aPersistenceLayer. The persistence layer that wraps up access to the database.
+*/
+void CCntStateMachine::ConstructL(CPersistenceLayer& aPersistenceLayer)
+	{
+	iState = CStateClosed::NewL(*this, aPersistenceLayer);
+	CleanupStack::PushL(iState);
+	// The order in which states are appended must be in sync with
+	// the declaration order of the state enum.
+	iStateArray.AppendL(iState); 
+	CleanupStack::Pop(iState);
+	
+	iStateArray.AppendL(CStateTablesClosed::NewL(*this, aPersistenceLayer)); 
+	iStateArray.AppendL(CStateWritable	  ::NewL(*this, aPersistenceLayer));
+	iStateArray.AppendL(CStateOpening 	  ::NewL(*this, aPersistenceLayer));
+	iStateArray.AppendL(CStateTransaction ::NewL(*this, aPersistenceLayer));
+	iStateArray.AppendL(CStateBackupRestore::NewL(*this, aPersistenceLayer));
+	}
+
+/**
+ Get the current active state		
+ 
+ @return The current active state
+*/ 
+CState& CCntStateMachine::CurrentState()
+	{
+	return *iState;
+	}
+	
+/** 
+ Get the transaction lock	
+ 
+ @return The Transaction Lock object
+*/
+CTransactionLock& CCntStateMachine::TransactionLockL()
+	{
+	if (!iTransLock)
+		{
+		iTransLock = CTransactionLock::NewL(*this);			
+		}
+
+	return *iTransLock;
+	}
+
+/** 
+ Get the Database Manager
+ 
+ @return the Contact Database Manager object
+*/
+CCntDbManager& CCntStateMachine::DbManager()
+	{
+	return iDbManager;
+	}
+
+/**
+ StateMachine constructor
+ 
+ @param aDbManager The Database Manager.
+*/
+CCntStateMachine::CCntStateMachine(CCntDbManager& aDbManager)
+	:
+  	iDbManager(aDbManager),
+  	iLowDisk(EFalse),
+  	iAsyncActivity(EFalse)
+	{
+	// Nothing to do.
+	}
+
+/**
+ Used for debugging the transition between state. 
+ Define __STATE_MACHINE_DEBUG__ in the project definition file (mmp).
+ 
+ @param aState The state which is becoming active
+ @return A descriptor containing the active state name.
+*/ 
+#ifdef __STATE_MACHINE_DEBUG__
+const TDesC& CCntStateMachine::StateName(CState& aState)
+	{
+	_LIT(KStateClosedName,   	"EStateClosed");
+	_LIT(KStateTablesClosed, 	"EStateTablesClosed");
+	_LIT(KStateWritableName, 	"EStateWritable");
+	_LIT(KStateOpeningName,  	"EStateOpening");
+	_LIT(KStateTransactionName, "EStateTransaction");
+	_LIT(KStateBackupRestoreName, "EStateBackupRestore");
+	_LIT(KStateUnknownName,  	"Unknown State");
+
+	if (&aState == &StateClosed())
+		{
+		return KStateClosedName();
+		}
+		
+	if (&aState == &StateTablesClosed())
+		{
+		return KStateTablesClosed();	
+		}
+		
+	if (&aState == &StateWritable())
+		{
+		return KStateWritableName();
+		}
+
+	if (&aState == &StateOpening())
+		{
+		return KStateOpeningName();
+		}
+
+	if (&aState == &StateTransaction())
+		{
+		return KStateTransactionName();
+		}
+
+	if (&aState == &StateBackupRestore())
+		{
+		return KStateBackupRestoreName();
+		}
+
+	return KStateUnknownName();
+	}
+#endif
+
+/** 
+ Set the active state in the state machine	
+ 
+ @param aState The state which is becoming active
+*/
+void CCntStateMachine::SetCurrentStateL(CState& aState)
+	{
+	
+#ifdef __STATE_MACHINE_DEBUG__
+	RDebug::Print(_L("[CNTMODEL] STA: %S --> %S\r\n"),	&CCntStateMachine::StateName(*iState), &CCntStateMachine::StateName(aState));
+#endif
+
+	iState = &aState;
+	// Process any requests in the Store on each state change
+	// The state may have changed to one where queued requests 
+	// can now be processed.
+	if(ReqStoreL().IsEmpty() == EFalse)
+		{
+		iReqStore->ActivateRequestsL();	
+		}
+	}
+
+/** 
+ This is the interface to the state machine (used by the session class). 
+ The state machine is asked to process a request by the session. It does this
+ by passing the  current active state to the request object (VisitStateL). 
+ The request then calls AcceptRequest on the current active state object. The
+ state and the request are completely decoupled.
+ 
+ Caller of ProcessRequestL should assume transfer request ownership unless 
+ ProcessRequestL leaves.  In the event ProcessRequestL leaves, the original owner
+ of the request is responsible for the cleanup of the request.  
+ 
+ Note that if the request is to be transferred to another object, ensure once the 
+ transfer has taken place, no leave should occur, as this will trigger both the new 
+ owner and the original owner to cleanup the request.
+ 
+ @param aRequest A request that is being processed.
+*/ 
+void CCntStateMachine::ProcessRequestL(CCntRequest* aRequest)
+    {
+	// Obtained ownership of the request object.  It is the responsibility
+	// of this function to ensure the request is properly disposed after being 
+	// processed unless leave occurs.
+    TAccept result = aRequest->VisitStateL(*iState);
+
+    switch(result)
+        {
+        case EDeferred:
+            // The visited state cannot process the request at the moment.  Activate the 
+            // request's timer and transfer ownership of the request to the request store.  
+            // Hopefully, a state change happens before timeout occurs, when the new state 
+            // will attempt to process all deferred request.  If timeout occurs before 
+            // a change of state, the request will be completed with its' timeout error code.  
+            // This timeout error code can be set thru the CCntRequest API SetTimeOutError.
+            aRequest->ActivateTimeOutL(ReqStoreL());
+            ReqStoreL().AppendL(aRequest);  // ownership transferred
+            break;
+
+        case EProcessed:
+			// The request has been processed by the visited state - nothing more
+			// to do, except to destroy the request now
+            delete aRequest;
+            break;
+
+        case EOwnershipPassed:
+            // the request has been hi-jacked by the visited state, the new owner will
+            // assume responsibility of cleanup, no need to do anything here.
+        default:
+            break;
+        }
+    }
+
+
+/**
+ Allow the active state to process the event. The only state that actually
+ processes events is the Transaction State. All other states (via the base state CState) 
+ simply propagate the event back to the CCntDbManager, from where it is propogated to
+ each CCntSession contained within the CCntDbManager.
+ 
+ @param aEvent The database event that is being propagated.
+*/ 
+void CCntStateMachine::HandleDatabaseEventL(TContactDbObserverEvent aEvent)
+	{
+	#if defined(__PROFILE_DEBUG__)
+		RDebug::Print(_L("[CNTMODEL] MTD: CCntStateMachine::HandleDatabaseEventL"));
+	#endif 
+	
+	iState->HandleDatabaseEventL(aEvent);
+	}
+
+/** 
+ Create the state machines request store. When a request cannot be processed in
+ the active state, it is deferred until the active state changes or a contact item is unlocked.
+ That is the request in this ReqStore is processed by the state machine - again - after either 
+ a state change or unlock operation. 
+ The store takes a reference to the CCntStateMachine in order to ProcessRequestL
+ 
+ @return The Request Store  	
+*/ 
+CRequestStore& CCntStateMachine::ReqStoreL()
+	{
+	if (!iReqStore)
+		{
+		iReqStore = CRequestStore::NewL(*this);	
+		}
+	return *iReqStore;	
+	}