phonebookengines/contactsmodel/cntsrv/src/CCntStateMachine.cpp
changeset 0 e686773b3f54
child 6 e8e3147d53eb
equal deleted inserted replaced
-1:000000000000 0:e686773b3f54
       
     1 // Copyright (c) 2006-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 /**
       
    17  @file
       
    18  @internalTechnology
       
    19  @released 
       
    20 */
       
    21 
       
    22 #include "CCntStateMachine.h"
       
    23 #include "CCntRequest.h"
       
    24 #include "CCntDbManager.h"
       
    25 #include "CCntBackupRestoreAgent.h"
       
    26 #include "persistencelayer.h"
       
    27 #include "CntSpeedDials.h"
       
    28 #include <cntfldst.h> 		// for ccontacttextfield
       
    29 #include <cntfield.h> 		// for ccontacttextfield
       
    30 #include "CNTSTD.H"   		// for Panic codes
       
    31 
       
    32 // Event related headers
       
    33 #include <cntdbobs.h> 	    // for ccontactdbobserver
       
    34 #include "CCntServer.h"     // for KCntNullConnectionId.
       
    35 #include "CCntEventQueue.h" // for KMaxNumberOfEventsInEventQueue, KCntEventGranularity
       
    36 #include "CCntLogger.h"  
       
    37 
       
    38 // Require SQL header for leave code checking
       
    39 #include <sqldb.h>
       
    40 	
       
    41 /** 
       
    42 CState - Base state destructor, default implementataion
       
    43 */
       
    44 CState::~CState() 
       
    45 	{}
       
    46 
       
    47 /**
       
    48  CState constructor. 
       
    49  The main purpose of the CState class is to define the state transition table.
       
    50  The CState class implements the most common state behaviour for each request object, 
       
    51  in the overridden AcceptRequestL method.
       
    52  Note there is no NewL method on the CState class. It is not meant to be instantiated.
       
    53  
       
    54  @param aStateMachine The statemachine object that is used for state transitions
       
    55  @param aPersistenceLayer The persistence layer that allows provides database access
       
    56  
       
    57 */	
       
    58 CState::CState(CCntStateMachine& aStateMachine, CPersistenceLayer&	aPersistenceLayer)
       
    59 :iStateMachine(aStateMachine), iPersistenceLayer(aPersistenceLayer)
       
    60 	{}
       
    61 
       
    62 /**
       
    63  TransactionStartLC is called from many derived states. The session that started the
       
    64  transaction is remembered in order to only allow that session perform database operations
       
    65  during the transaction. 
       
    66  Transaction rollback is pushed onto the clean up stack in case of a leave should occur before the
       
    67  transaction is committed. If the transaction can not be committed in full, then none of
       
    68  the transaction should be committed.
       
    69  
       
    70  @param aSessionId The unique ID of session that is moving the state machine into a transaction state.
       
    71 */
       
    72 void CState::TransactionStartLC(TUint aSessionId)
       
    73 	{
       
    74 	iCurrentTransactionSessionId = aSessionId;
       
    75 	iPersistenceLayer.TransactionManager().StartTransactionL();
       
    76 	CleanupStack::PushL(TCleanupItem(CState::CleanupTransactionRollback, this));
       
    77 	}
       
    78 
       
    79 /** 
       
    80  Rollback a transaction after an leave has occured.
       
    81  None of the transaction is committed to the database
       
    82  
       
    83  @param aState The state in which the leave occured
       
    84 */
       
    85 void CState::CleanupTransactionRollback(TAny* aState)
       
    86   	{
       
    87   	TRAP_IGNORE(static_cast<CState*>(aState)->RollbackTransAndRecoverL(EFalse));
       
    88   	}
       
    89 
       
    90 /**
       
    91  Commit the current transaction.
       
    92 */
       
    93 void CState::TransactionCommitLP()
       
    94 	{
       
    95 	iPersistenceLayer.TransactionManager().CommitCurrentTransactionL(iCurrentTransactionSessionId);
       
    96 	iCurrentTransactionSessionId = 0;
       
    97 	CleanupStack::Pop(); // CleanupTransactionRollback
       
    98 	}
       
    99 
       
   100 /**
       
   101  Clean up the transaction after a leave occurs. 
       
   102 */
       
   103 void CState::RollbackTransAndRecoverL(const TBool aNotification)
       
   104 	{
       
   105   	// Some operation has left before a commit could be called.
       
   106   	iPersistenceLayer.TransactionManager().RollbackCurrentTransactionL(iCurrentTransactionSessionId);
       
   107 	iCurrentTransactionSessionId = 0;
       
   108 	
       
   109 	iPersistenceLayer.ContactsFileL().CloseTablesL(!aNotification);
       
   110 	iPersistenceLayer.ContactsFileL().OpenTablesL(!aNotification);
       
   111 	iStateMachine.SetCurrentStateL(iStateMachine.StateWritable());			
       
   112 	}
       
   113 
       
   114 /* Note: The following methods implement default 
       
   115    AcceptRequestL behaviour for all states derived 
       
   116    from Parent Class CState
       
   117 */
       
   118 
       
   119 /* 
       
   120  Default behaviour: The file has already been opened 
       
   121  so complete KErrNone.
       
   122  
       
   123  @param aRequest Open database request object
       
   124  @return TAccept EProcessed - finished processing request
       
   125 */ 
       
   126 TAccept CState::AcceptRequestL(CReqAsyncOpen* aRequest)
       
   127 	{
       
   128 	aRequest->Complete();
       
   129 	return EProcessed;
       
   130 	}
       
   131 
       
   132 /**
       
   133  Default behaviour is to defer the request
       
   134 
       
   135  @param aRequest Update contact item request object
       
   136  @return EDeferred if the request is not processed, EProcessed if the request is
       
   137          completed with timeout error.
       
   138  @see CState::DeferRequest 
       
   139 */
       
   140 TAccept CState::AcceptRequestL(CReqUpdateCnt* aRequest)
       
   141 	{
       
   142 	return DeferRequest(aRequest);
       
   143 	}
       
   144 
       
   145 /**
       
   146  Default behaviour is to defer the request
       
   147 
       
   148  @param aRequest Commit contact item request object
       
   149  @return EDeferred if the request is not processed, EProcessed if the request is
       
   150          completed with timeout error.
       
   151  @see CState::DeferRequest 
       
   152 */
       
   153 TAccept CState::AcceptRequestL(CReqCommitCnt* aRequest)
       
   154 	{
       
   155 	return DeferRequest(aRequest);
       
   156 	}
       
   157 	
       
   158 
       
   159 /** 
       
   160  Default behaviour is to allow read access on the database	
       
   161  Open is the same as read but with locking - does not change the database	
       
   162 
       
   163  @param aRequest Open contact item request object
       
   164  @return EProcessed if the request is completed or one of the values returned by 
       
   165          CState::DeferRequest if the database is currently locked.
       
   166  @see CState::DeferRequest 
       
   167 */
       
   168 TAccept CState::AcceptRequestL(CReqOpenCnt* aRequest)
       
   169 	{
       
   170 	if (iStateMachine.TransactionLockL().LockLX(aRequest->SessionId(), aRequest->CntItemId()) == KErrInUse)
       
   171 		{
       
   172 		// The contact item has been locked by another session
       
   173 		aRequest->SetTimeOutError(KErrInUse);
       
   174 		return DeferRequest(aRequest);
       
   175 		}
       
   176 
       
   177 	CContactItem* cntItem = iPersistenceLayer.PersistenceBroker().ReadLC(aRequest->CntItemId(), aRequest->ItemViewDef(), EPlAllInfo, aRequest->SessionId(), ETrue);
       
   178 	aRequest->CompleteL(*cntItem);
       
   179 	CleanupStack::PopAndDestroy(cntItem); 
       
   180 	CleanupStack::Pop(); // we do not destroy it since that would trigger the leave mechanism and unlock the record
       
   181 	return EProcessed;
       
   182 	}
       
   183 
       
   184 /**
       
   185  Default behaviour is to allow read access on the database.
       
   186  Read Contact does not change the database
       
   187  
       
   188  @param aRequest Read contact item request object
       
   189  @return TAccept EProcessed - finished processing request
       
   190 */ 
       
   191 TAccept CState::AcceptRequestL(CReqReadCnt* aRequest)
       
   192 	{
       
   193 	CContactItem* cntItem = iPersistenceLayer.PersistenceBroker().ReadLC(aRequest->CntItemId(), aRequest->ItemViewDef(), EPlAllInfo, aRequest->SessionId());
       
   194 	aRequest->CompleteL(*cntItem);
       
   195 	CleanupStack::PopAndDestroy(cntItem);
       
   196 	return EProcessed;		
       
   197 	}		
       
   198 
       
   199 /**
       
   200  Default behaviour is to defer the request
       
   201 
       
   202  @param aRequest Delete contact item request object
       
   203  @return EDeferred if the request is not processed, EProcessed if the request is
       
   204          completed with timeout error.
       
   205  @see CState::DeferRequest 
       
   206 */
       
   207 TAccept CState::AcceptRequestL(CReqDeleteCnt* aRequest)
       
   208 	{
       
   209 	return DeferRequest(aRequest);	
       
   210 	}
       
   211 	
       
   212 /**
       
   213  Default behaviour is to close the contact item by removing the locking 
       
   214  The lock was added during from an Open Contact Request
       
   215  If the contact id is supplied, explicity unlock that contact item, otherwise
       
   216  unlock the last contact item locked by the session
       
   217 
       
   218  @param aRequest Close contact item request object
       
   219  @return TAccept EProcessed - finished processing request
       
   220 */  
       
   221 TAccept CState::AcceptRequestL(CReqCloseCnt* aRequest)
       
   222 	{
       
   223 	if (aRequest->CntItemId() > KNullContactId)
       
   224 		{
       
   225 		aRequest->Complete(iStateMachine.TransactionLockL().UnLockL(aRequest->SessionId(), aRequest->CntItemId()));	
       
   226 		}
       
   227 	else
       
   228 		{
       
   229 		iStateMachine.TransactionLockL().UnlockLastLockedContactL(aRequest->SessionId());
       
   230 		aRequest->Complete(KErrNone);
       
   231 		}
       
   232 	return EProcessed;		
       
   233 	}
       
   234 
       
   235 /**
       
   236  Default behaviour is to always allow a session to unlock any contact 
       
   237  items that remain locked for that session before the session is closed
       
   238 
       
   239  @param aRequest Unlock all contact items request object  
       
   240  @return TAccept EProcessed - finished processing request
       
   241 */  
       
   242 TAccept CState::AcceptRequestL(CReqInternalSessionUnlock* aRequest)
       
   243 	{
       
   244 	iStateMachine.TransactionLockL().UnLockAllL(aRequest->SessionId());
       
   245 	aRequest->Complete(KErrNone);
       
   246 	return EProcessed;		
       
   247 	}
       
   248 
       
   249 /**
       
   250  Default behaviour is to defer the request
       
   251 
       
   252  @param Create contact item request object
       
   253  @return EDeferred if the request is not processed, EProcessed if the request is
       
   254          completed with timeout error.
       
   255  @see CState::DeferRequest 
       
   256 */
       
   257 TAccept CState::AcceptRequestL(CReqCreateCnt* aRequest)
       
   258 	{
       
   259 	return DeferRequest(aRequest);	
       
   260 	}
       
   261 
       
   262 /**
       
   263  The default behaviour for Cancelling an Asyncronous Open database command
       
   264  is to accept the file has been opened.
       
   265  
       
   266  @param aRequest Cancel database open request object 
       
   267  @return TAccept EProcessed - finished processing request
       
   268 */ 
       
   269 TAccept CState::AcceptRequestL(CReqCancelAsyncOpen* aRequest)
       
   270 	{
       
   271 	aRequest->Complete();
       
   272 	return EProcessed;	
       
   273 	}
       
   274 
       
   275 
       
   276 /**
       
   277  Default behaviour is to defer the request
       
   278    
       
   279  @param aRequest Close contact database tables request object
       
   280  @return EDeferred if the request is not processed, EProcessed if the request is
       
   281          completed with timeout error.
       
   282  @see CState::DeferRequest 
       
   283 */
       
   284 TAccept CState::AcceptRequestL(CReqCloseTables* aRequest)
       
   285   	{
       
   286  	return DeferRequest(aRequest);	
       
   287   	}
       
   288 
       
   289 /**
       
   290  Default behaviour is to defer the request
       
   291 
       
   292  @param aRequest ReOpen contact database tables request object
       
   293  @return EDeferred if the request is not processed, EProcessed if the request is
       
   294          completed with timeout error.
       
   295  @see CState::DeferRequest 
       
   296 */
       
   297 TAccept CState::AcceptRequestL(CReqReOpen* aRequest)
       
   298 	{
       
   299 	return DeferRequest(aRequest);	
       
   300 	}
       
   301 
       
   302 /**
       
   303  Default behaviour is to defer the request
       
   304 
       
   305  @param aRequest Begin transaction request object
       
   306  @return EDeferred if the request is not processed, EProcessed if the request is
       
   307          completed with timeout error.
       
   308  @see CState::DeferRequest 
       
   309 */
       
   310 TAccept CState::AcceptRequestL(CReqDbBeginTrans* aRequest)
       
   311 	{
       
   312 	return DeferRequest(aRequest);	
       
   313 	}
       
   314 
       
   315 /**
       
   316  Default behaviour is to defer the request
       
   317 
       
   318  @param aRequest Commit transaction request object
       
   319  @return EDeferred if the request is not processed, EProcessed if the request is
       
   320          completed with timeout error.
       
   321  @see CState::DeferRequest 
       
   322 */
       
   323 TAccept CState::AcceptRequestL(CReqDbCommitTrans* aRequest)
       
   324 	{
       
   325 	return DeferRequest(aRequest);	
       
   326 	}
       
   327 
       
   328 /**
       
   329  Default behaviour is to defer the request
       
   330 
       
   331  @param aRequest Rollback transaction request object
       
   332  @return EDeferred if the request is not processed, EProcessed if the request is
       
   333          completed with timeout error.
       
   334  @see CState::DeferRequest 
       
   335  */
       
   336 TAccept CState::AcceptRequestL(CReqDbRollbackTrans* aRequest)
       
   337 	{
       
   338 	return DeferRequest(aRequest);	
       
   339 	}
       
   340 
       
   341 /**
       
   342  Default behaviour is to defer the request
       
   343 
       
   344  @param aRequest Notification of database backup/restore request object
       
   345  @return EDeferred if the request is not processed, EProcessed if the request is
       
   346          completed with timeout error.
       
   347  @see CState::DeferRequest 
       
   348 */
       
   349 TAccept CState::AcceptRequestL(CReqBackupRestoreBegin* aRequest)
       
   350 	{
       
   351 	return DeferRequest(aRequest);	
       
   352 	}
       
   353 
       
   354 /**
       
   355  Default behaviour is to complete the request
       
   356 
       
   357  @param aRequest Notification that a backup/restore has finished request object
       
   358  @return TAccept EProcessed - finished processing request
       
   359 */
       
   360 TAccept CState::AcceptRequestL(CReqBackupRestoreEnd* aRequest)
       
   361    	{
       
   362   	// In most cases no backup/restore will be in progress so by default
       
   363   	// complete this request.
       
   364   	aRequest->Complete();
       
   365   	return EProcessed;
       
   366   	}
       
   367   
       
   368 /**
       
   369  Default behaviour is set an internal low disk flag to true
       
   370  
       
   371  @param aRequest Notification of low disk space request object
       
   372  @return TAccept EProcessed - finished processing request
       
   373 */  
       
   374 TAccept CState::AcceptRequestL(CReqDiskSpaceLow* aRequest)
       
   375   	{
       
   376   	iStateMachine.SetLowDisk(ETrue);
       
   377   	aRequest->Complete();
       
   378   	return EProcessed;
       
   379   	}
       
   380 
       
   381 /**
       
   382  Default behaviour is to set an internal low disk flag to false
       
   383 
       
   384  @param aRequest Notification disk space is normal request object
       
   385  @return TAccept EProcessed - finished processing request
       
   386 */
       
   387 TAccept CState::AcceptRequestL(CReqDiskSpaceNormal* aRequest)
       
   388   	{
       
   389   	iStateMachine.SetLowDisk(EFalse);
       
   390   	aRequest->Complete();
       
   391   	return EProcessed;
       
   392   	}
       
   393 
       
   394 /**
       
   395  Default behaviour is set an internal async activity flag to true
       
   396  
       
   397  @param aRequest Notification of async activity request object
       
   398  @return TAccept EProcessed - finished processing request
       
   399 */  
       
   400 TAccept CState::AcceptRequestL(CReqAsyncActivity* aRequest)
       
   401   	{
       
   402   	iStateMachine.SetAsyncActivity(ETrue);
       
   403   	aRequest->Complete();
       
   404   	return EProcessed;
       
   405   	}
       
   406 
       
   407 /**
       
   408  Default behaviour is to set an internal async activity flag to false
       
   409 
       
   410  @param aRequest Notification of no async activity request object
       
   411  @return TAccept EProcessed - finished processing request
       
   412 */
       
   413 TAccept CState::AcceptRequestL(CReqNoAsyncActivity* aRequest)
       
   414   	{
       
   415   	iStateMachine.SetAsyncActivity(EFalse);
       
   416   	aRequest->Complete();
       
   417   	return EProcessed;
       
   418   	}
       
   419 
       
   420 /**
       
   421  Default behaviour is to defer the request
       
   422 
       
   423  @param aRequest Speed dial request object
       
   424  @return EDeferred if the request is not processed, EProcessed if the request is
       
   425          completed with timeout error.
       
   426  @see CState::DeferRequest 
       
   427 */
       
   428 TAccept CState::AcceptRequestL(CReqSetSpeedDial* aRequest)
       
   429 	{
       
   430 	return DeferRequest(aRequest);	
       
   431 	}
       
   432 
       
   433 /**
       
   434  Default behaviour is to defer the request
       
   435 
       
   436  @param aRequest Own card request object
       
   437  @return EDeferred if the request is not processed, EProcessed if the request is
       
   438          completed with timeout error.
       
   439  @see CState::DeferRequest 
       
   440 */
       
   441 TAccept CState::AcceptRequestL(CReqSetOwnCard* aRequest)
       
   442 	{
       
   443 	return DeferRequest(aRequest);	
       
   444 	}
       
   445 
       
   446 
       
   447 /**
       
   448  Don't do anything with the event here. Simply propagate the event to the dbmanager
       
   449  Any state that wishes to handle the event implements it's own overwritten 
       
   450  HandleDatabaseEventL method. The only state
       
   451  that actually implements this is the Transaction State.
       
   452  
       
   453  @param aEvent Database event generated in the Persistence Layer
       
   454 */ 
       
   455 void CState::HandleDatabaseEventL(TContactDbObserverEvent aEvent)
       
   456 	{
       
   457 	iStateMachine.DbManager().HandleDatabaseEventL(aEvent);
       
   458 	}
       
   459 
       
   460 /**
       
   461  Sets a timeout error code on the request. This error code is retrieved from the 
       
   462  derived state class via a call to TimeOutErrorCode() 
       
   463  if the derived state class does not implement a overridden TimeOutErrorCode(), 
       
   464  the CState::TimeOutErrorCode() is used.
       
   465  
       
   466  Calls CState::DeferRequest to determine if the request should be deferred or not
       
   467  depending on the request's timer status.
       
   468 
       
   469  @param aRequest The request on which the timeout error is set.
       
   470  @return EDeferred if the request is not processed, EProcessed if the request is
       
   471          completed with timeout error.
       
   472  @see CState::DeferRequest 
       
   473 */
       
   474 TAccept CState::DeferWithTimeOutError(CCntRequest* aRequest)
       
   475 	{
       
   476 	// use the time out error code specified with the current state
       
   477 	aRequest->SetTimeOutError(TimeOutErrorCode());
       
   478 
       
   479     return DeferRequest(aRequest);
       
   480 	}
       
   481 
       
   482 /**
       
   483  Determines if an un-processed request should be completed with timeout error or if it 
       
   484  should be re-tried again later, depending on the current status of the request's timer.  
       
   485 
       
   486  @param aRequest The request failed to be processed by the current state
       
   487  @return EDeferred - if the request's timer has not expired, the request can be processed again at the 
       
   488                      next opportunity.
       
   489          EProcessed - if the request's timer has expired and is completed with timeout 
       
   490                       error.
       
   491 */	
       
   492 TAccept CState::DeferRequest(CCntRequest* aRequest)
       
   493     {
       
   494 	// request still cannot be processed, check if the timer on the request
       
   495 	// has expired yet
       
   496 	if (aRequest->TimeOut() <= 0)
       
   497         {
       
   498         // timer expired, request should be completed with timeout error
       
   499     	aRequest->Complete(aRequest->TimeOutError());
       
   500     	return EProcessed;
       
   501         }
       
   502 
       
   503     // timer still valid, as request cannot be processed now, signal to re-try again later
       
   504     return EDeferred;	
       
   505     }
       
   506 
       
   507 /** 
       
   508  Returns the default timeout - KErrNotReady
       
   509 */
       
   510 TInt CState::TimeOutErrorCode()  	
       
   511 	{
       
   512 	return KErrNotReady;
       
   513 	}
       
   514 
       
   515 
       
   516 /** 
       
   517  CStateClosed Class NewL factory constructor
       
   518  @see CState constructor
       
   519 */
       
   520 CStateClosed* CStateClosed::NewL(CCntStateMachine& aStateMachine, CPersistenceLayer& aPersistenceLayer)
       
   521 	{
       
   522 	CStateClosed* stateClosed = new (ELeave) CStateClosed(aStateMachine, aPersistenceLayer);
       
   523 	return stateClosed;
       
   524 	}
       
   525 
       
   526 /** 
       
   527  CStateClosed Class constructor
       
   528  @see CState constructor
       
   529 */
       
   530 CStateClosed::CStateClosed(CCntStateMachine& aStateMachine, CPersistenceLayer& aPersistenceLayer)
       
   531 :CState(aStateMachine, aPersistenceLayer)
       
   532 	{}
       
   533 
       
   534 /** 
       
   535  CStateClosed Class destructor
       
   536  @see CState destructor
       
   537 */
       
   538 CStateClosed::~CStateClosed() {}
       
   539 
       
   540 
       
   541 /**
       
   542  Process an open request in the closed state.
       
   543  Note on the State design pattern:
       
   544  Unlike the common state design pattern, most of the work is done in
       
   545  the opening state and not in the closed state before the state transition.
       
   546  The reason for this is re-use of code. 
       
   547  
       
   548  @param aRequest Open database request object
       
   549  @return TAccept EProcessed if finished processing request
       
   550  		 	     EDeferred if the request was not processed 
       
   551 */		
       
   552 TAccept CStateClosed::AcceptRequestL(CReqAsyncOpen* aRequest)
       
   553 	{
       
   554 	iStateMachine.SetCurrentStateL(iStateMachine.StateOpening());
       
   555 	return iStateMachine.CurrentState().AcceptRequestL(aRequest);
       
   556 	}
       
   557 
       
   558 /**
       
   559  We can only process re-open tables requests in the CStateTablesClosed
       
   560  which means that this request must be preceeded by both a CReqAsyncOpen &
       
   561  a CReqCloseTables. The correct behaviour is to defer the request with
       
   562  the default CStateClosed timeout error.
       
   563  
       
   564  @param aRequest ReOpen database tables request object 
       
   565  @return EDeferred if the request is not processed, EProcessed if the request is
       
   566          completed with timeout error.
       
   567  @see CState::DeferWithTimeOutError
       
   568 */
       
   569 
       
   570 TAccept CStateClosed::AcceptRequestL(CReqReOpen* aRequest)
       
   571 	{
       
   572 	return DeferWithTimeOutError(aRequest);
       
   573 	}
       
   574 
       
   575 
       
   576 /** 
       
   577  Overridden read-only operations from base class: can't read from the database
       
   578  while in the Closed state so defer these requests.
       
   579  
       
   580  @param aRequest Read contact item request object  
       
   581  @return EDeferred if the request is not processed, EProcessed if the request is
       
   582          completed with timeout error.
       
   583  @see CState::DeferWithTimeOutError
       
   584 */
       
   585 TAccept CStateClosed::AcceptRequestL(CReqReadCnt* aRequest)
       
   586 	{
       
   587 	return DeferWithTimeOutError(aRequest);
       
   588 	}
       
   589 
       
   590 /**
       
   591  Defer Update requests with the default error for CStateClosed.
       
   592  Note: This default error code is taken from the original
       
   593  contacts model.
       
   594  
       
   595  @param aRequest Update contact item request object  
       
   596  @return EDeferred if the request is not processed, EProcessed if the request is
       
   597          completed with timeout error.
       
   598  @see CState::DeferWithTimeOutError
       
   599 */ 
       
   600 TAccept CStateClosed::AcceptRequestL(CReqUpdateCnt* aRequest)
       
   601 	{
       
   602 	return DeferWithTimeOutError(aRequest);
       
   603 	}
       
   604 
       
   605 /** 
       
   606  Defer Commit requests
       
   607  The default error for CStateClosed has been taken from the original 
       
   608  contacts model
       
   609 
       
   610  @param aRequest Commit contact item request object  
       
   611  @return EDeferred if the request is not processed, EProcessed if the request is
       
   612          completed with timeout error.
       
   613  @see CState::DeferWithTimeOutError
       
   614 */
       
   615 TAccept CStateClosed::AcceptRequestL(CReqCommitCnt* aRequest)
       
   616 	{
       
   617 	return DeferWithTimeOutError(aRequest);
       
   618 	}
       
   619 
       
   620 /** 
       
   621  Defer Delete requests
       
   622  The default error for CStateClosed has been taken from the original 
       
   623  contacts model
       
   624 
       
   625  @param aRequest Delete contact item request object  
       
   626  @return EDeferred if the request is not processed, EProcessed if the request is
       
   627          completed with timeout error.
       
   628  @see CState::DeferWithTimeOutError
       
   629 */
       
   630 TAccept CStateClosed::AcceptRequestL(CReqDeleteCnt* aRequest)
       
   631 	{
       
   632 	return DeferWithTimeOutError(aRequest);
       
   633 	}
       
   634 
       
   635 /** 
       
   636  Defer Create requests
       
   637  The default error for CStateClosed has been taken from the original 
       
   638  contacts model
       
   639 
       
   640  @param aRequest Create contact item request object  
       
   641  @return EDeferred if the request is not processed, EProcessed if the request is
       
   642          completed with timeout error.
       
   643  @see CState::DeferWithTimeOutError
       
   644 */
       
   645 TAccept CStateClosed::AcceptRequestL(CReqCreateCnt* aRequest)
       
   646 	{
       
   647 	return DeferWithTimeOutError(aRequest);
       
   648 	}
       
   649 
       
   650 /** 
       
   651  Defer Open requests
       
   652  The default error for CStateClosed has been taken from the original 
       
   653  contacts model
       
   654  The default behaviour of the parent class CStateis to process an open request, the Closed State	
       
   655  defers this request
       
   656 
       
   657  @param aRequest Commit contact item request object  
       
   658  @return EDeferred if the request is not processed, EProcessed if the request is
       
   659          completed with timeout error.
       
   660  @see CState::DeferWithTimeOutError
       
   661 */
       
   662 TAccept CStateClosed::AcceptRequestL(CReqOpenCnt* aRequest)
       
   663 	{
       
   664 	return DeferWithTimeOutError(aRequest);
       
   665 	}
       
   666 
       
   667 /**
       
   668  Defer the Close tables request
       
   669  The tables are closed but so is the file. 
       
   670  To close the tables the file must be open
       
   671  	
       
   672  @param aRequest Close database tables request object  
       
   673  @return EDeferred if the request is not processed, EProcessed if the request is
       
   674          completed with timeout error.
       
   675  @see CState::DeferWithTimeOutError
       
   676 */
       
   677 TAccept CStateClosed::AcceptRequestL(CReqCloseTables* aRequest)
       
   678   	{
       
   679   	return DeferWithTimeOutError(aRequest);
       
   680   	}
       
   681   
       
   682 /**
       
   683  Defer begin transaction requests  
       
   684  The default behaviour of the parent class CState is to process a begin transaction request, 
       
   685  the Closed State defers this request
       
   686 
       
   687  @param aRequest Begin transaction request object  
       
   688  @return EDeferred if the request is not processed, EProcessed if the request is
       
   689          completed with timeout error.
       
   690  @see CState::DeferWithTimeOutError
       
   691 */
       
   692 TAccept CStateClosed::AcceptRequestL(CReqDbBeginTrans* aRequest)
       
   693 	{
       
   694 	return DeferWithTimeOutError(aRequest);
       
   695 	}
       
   696 
       
   697 /**
       
   698  Defer commit transaction requests  
       
   699  The default behaviour of the parent class CState is to process a commit transaction request, 
       
   700  the Closed State defers this request
       
   701 
       
   702  @param aRequest Commit transaction request object  
       
   703  @return EDeferred if the request is not processed, EProcessed if the request is
       
   704          completed with timeout error.
       
   705  @see CState::DeferWithTimeOutError
       
   706 */
       
   707 TAccept CStateClosed::AcceptRequestL(CReqDbCommitTrans* aRequest)
       
   708 	{
       
   709 	return DeferWithTimeOutError(aRequest);
       
   710 	}
       
   711 
       
   712 /**
       
   713  Defer rollback transaction requests  
       
   714  The default behaviour of the parent class CState is to process a rollback transaction request, 
       
   715  the Closed State defers this request
       
   716 
       
   717  @param aRequest Rollback transaction request object  
       
   718  @return EDeferred if the request is not processed, EProcessed if the request is
       
   719          completed with timeout error.
       
   720  @see CState::DeferWithTimeOutError
       
   721 */
       
   722 TAccept CStateClosed::AcceptRequestL(CReqDbRollbackTrans*  aRequest)
       
   723 	{
       
   724 	return DeferWithTimeOutError(aRequest);
       
   725 	}
       
   726 
       
   727 /**
       
   728  Defer set speed dial requests  
       
   729  The default behaviour of the parent class CState is to process a set speed dial request, 
       
   730  the Closed State defers this request
       
   731 
       
   732  @param aRequest Set speed dial request object  
       
   733  @return EDeferred if the request is not processed, EProcessed if the request is
       
   734          completed with timeout error.
       
   735  @see CState::DeferWithTimeOutError
       
   736 */
       
   737 TAccept CStateClosed::AcceptRequestL(CReqSetSpeedDial* aRequest)
       
   738 	{
       
   739 	return DeferWithTimeOutError(aRequest);
       
   740 	}
       
   741 
       
   742 /**
       
   743  Defer set own card requests  
       
   744  The default behaviour of the parent class CState is to process a set own card request, 
       
   745  the Closed State defers this request
       
   746 
       
   747  @param aRequest Set own card request object  
       
   748  @return EDeferred if the request is not processed, EProcessed if the request is
       
   749          completed with timeout error.
       
   750  @see CState::DeferWithTimeOutError
       
   751 */	
       
   752 TAccept CStateClosed::AcceptRequestL(CReqSetOwnCard* aRequest)
       
   753 	{
       
   754 	return DeferWithTimeOutError(aRequest);
       
   755 	}
       
   756 
       
   757 
       
   758 /**
       
   759  Backup/restore begin notification requests  
       
   760 
       
   761  @param aRequest backup/restore begin notification request object  
       
   762  @return TAccept EProcessed
       
   763 */	
       
   764 TAccept CStateClosed::AcceptRequestL(CReqBackupRestoreBegin* aRequest)
       
   765   	{
       
   766   	// Backup/restore can take place from this state without doing anything so
       
   767   	// simply complete request.
       
   768   	aRequest->Complete();
       
   769   	return EProcessed;
       
   770   	}
       
   771 
       
   772 
       
   773 /** 
       
   774  CStateTablesClosed Class NewL factory constructor
       
   775  @see CState constructor
       
   776 */  	
       
   777 CStateTablesClosed* CStateTablesClosed::NewL(CCntStateMachine& aStateMachine, CPersistenceLayer& aPersistenceLayer)
       
   778   	{
       
   779   	CStateTablesClosed* stateTablesClosed = new (ELeave) CStateTablesClosed(aStateMachine, aPersistenceLayer);
       
   780   	return stateTablesClosed;
       
   781   	}
       
   782   
       
   783 /** 
       
   784  CStateTablesClosed Class destructor
       
   785  @see CState Destructor
       
   786 */  	
       
   787 CStateTablesClosed::~CStateTablesClosed()
       
   788   	{
       
   789   	}
       
   790   
       
   791 /** 
       
   792  CStateTablesClosed Class constructor
       
   793  @see CState constructor
       
   794 */  	
       
   795 CStateTablesClosed::CStateTablesClosed(CCntStateMachine& aStateMachine, CPersistenceLayer& aPersistenceLayer) 
       
   796   :CStateClosed(aStateMachine, aPersistenceLayer)
       
   797   	{
       
   798   	}
       
   799   
       
   800   
       
   801 /**
       
   802  Accept re-open tables requests  
       
   803  The tables are closed. A re-open request will result in re-opening the tables.
       
   804    
       
   805   @param aRequest re-open tables request object  
       
   806   @return TAccept EProcessed - the request was processed 
       
   807 */
       
   808 TAccept CStateTablesClosed::AcceptRequestL(CReqReOpen* aRequest)
       
   809   	{
       
   810   	iPersistenceLayer.ContactsFileL().OpenTablesL(ETrue);
       
   811   	// We can only ever come into this state from the writable state
       
   812   	// as the database must have been opened
       
   813   	iStateMachine.SetCurrentStateL(iStateMachine.StateWritable());
       
   814   	aRequest->Complete();
       
   815   	return EProcessed;		
       
   816   	}
       
   817   
       
   818 /**
       
   819   Accept Open database requests  
       
   820   The tables are closed. An async open request will result in re-opening the
       
   821   tables.
       
   822     
       
   823   @param aRequest async open request object  
       
   824   @return TAccept EProcessed if finished processing request
       
   825 */	
       
   826 TAccept CStateTablesClosed::AcceptRequestL(CReqAsyncOpen* aRequest)
       
   827   	{	
       
   828   	iPersistenceLayer.ContactsFileL().OpenTablesL(ETrue);
       
   829   	// We can only ever come into this state from the writable state
       
   830   	// as the database must have been opened
       
   831   	iStateMachine.SetCurrentStateL(iStateMachine.StateWritable());
       
   832   	aRequest->Complete();
       
   833   	return EProcessed;
       
   834   	}
       
   835 
       
   836 /** 
       
   837  CStateOpening Class NewL factory constructor
       
   838  @see CState constructor
       
   839 */ 
       
   840 CStateOpening* CStateOpening::NewL(CCntStateMachine& aStateMachine, CPersistenceLayer& aPersistenceLayer)
       
   841 	{
       
   842 	CStateOpening* stateOpening = new (ELeave) CStateOpening(aStateMachine, aPersistenceLayer);
       
   843 	CleanupStack::PushL(stateOpening);
       
   844 	stateOpening->ConstructL();
       
   845 	CleanupStack::Pop(stateOpening);
       
   846 	return stateOpening;
       
   847 	}
       
   848 
       
   849 /** 
       
   850  CStateOpening ConstructL, gets and holds an instance of the persisitnce layer contact file interface
       
   851  and creates and ActiveLoop object which facilitates long running operations.  
       
   852  @see CState constructor
       
   853 */
       
   854 void CStateOpening::ConstructL()
       
   855 	{
       
   856 	iCntFile = &(iPersistenceLayer.ContactsFileL());
       
   857 	iActive = CActiveLoop::NewL();
       
   858 	}
       
   859 /** 
       
   860  CStateOpening constructor
       
   861  @see CState constructor
       
   862 */
       
   863 CStateOpening::CStateOpening(CCntStateMachine& aStateMachine, CPersistenceLayer& aPersistenceLayer)
       
   864 :CState(aStateMachine, aPersistenceLayer)
       
   865 	{
       
   866 	}
       
   867 
       
   868 /** 
       
   869  CStateOpening Class destructor
       
   870  @see CState destructor
       
   871 */	
       
   872 CStateOpening::~CStateOpening()
       
   873 	{
       
   874 	delete iActive;
       
   875 	delete iFileName;
       
   876 	iOpenReqsStore.ResetAndDestroy();
       
   877 	}
       
   878 
       
   879 /**
       
   880  Accept Open database requests  
       
   881  Opens the database file and tables.
       
   882   
       
   883  @param aRequest Open database request object  
       
   884  @return EOwnershipPassed 
       
   885  @see CStateOpening::OpenDatabaseFileL
       
   886 */
       
   887 TAccept CStateOpening::AcceptRequestL(CReqAsyncOpen* aRequest)
       
   888 	{
       
   889 	SetFileNameL(aRequest->FileName()); 
       
   890 	return OpenDatabaseFileL(aRequest);	
       
   891 	}
       
   892 
       
   893 /**
       
   894  Private member function that starts the database opening process.  
       
   895  This method may be called from several AcceptRequestL methods.
       
   896  
       
   897  @param aRequest Open database request object  
       
   898  @return EOwnershipPassed to indicate ownership of the open database request has 
       
   899          been transferred
       
   900 */
       
   901 TAccept CStateOpening::OpenDatabaseFileL(CCntRequest* aRequest, TBool aNotify)
       
   902 	{
       
   903 	// Add this request to the store where it will be completed on completion of 
       
   904 	// the open operaion
       
   905 	//
       
   906 	// Note that after the request is successfully transferred to the Request 
       
   907 	// Store, no leave can occur until the caller is notified that ownership
       
   908 	// of the request has been transferred.
       
   909 	iOpenReqsStore.AppendL(aRequest);
       
   910 
       
   911 	iNotify = aNotify;
       
   912 
       
   913 	// If we are not already processing an open request, start opening the file.
       
   914 	if (!iActive->IsActive())
       
   915 		{
       
   916 		InitialStep();
       
   917 		}
       
   918 
       
   919 	return EOwnershipPassed;
       
   920 	}
       
   921 
       
   922 /**
       
   923  Accept the Reopen tables request as the tables are being opened anyway
       
   924 
       
   925  @param aRequest Open database request object  
       
   926  @return EOwnershipPassed 
       
   927  @see CStateOpening::OpenDatabaseFileL
       
   928 */
       
   929 TAccept CStateOpening::AcceptRequestL(CReqReOpen* aRequest)
       
   930 	{
       
   931 	// Open the database and notify (ETrue) all session of the recovery 
       
   932 	return OpenDatabaseFileL(aRequest, ETrue);	
       
   933 	}
       
   934 
       
   935 /** 
       
   936  Explicit cancel of an asyncronous Open Database request
       
   937 
       
   938  @param aRequest Open database request object  
       
   939  @return TAccept EProcessed 
       
   940 */
       
   941 TAccept CStateOpening::AcceptRequestL(CReqCancelAsyncOpen* aRequest)
       
   942 	{
       
   943 	TInt max = iOpenReqsStore.Count();
       
   944 	for (TInt ii = 0; ii < max; ++ii)
       
   945 		{
       
   946 		if (aRequest->SessionId() == iOpenReqsStore[ii]->SessionId())
       
   947 			{
       
   948 			// If we only have one concurrent open request, cancel the open
       
   949 			// operation
       
   950 			if (max == 1)
       
   951 				{
       
   952 				iCntFile->Close();	
       
   953 				iActive->Cancel();
       
   954 				iStateMachine.SetCurrentStateL(iStateMachine.StateClosed());
       
   955 				}
       
   956 			
       
   957 			iOpenReqsStore[ii]->Complete(KErrCancel);
       
   958 			aRequest->Complete();
       
   959 			delete iOpenReqsStore[ii];
       
   960 			iOpenReqsStore.Remove(ii);
       
   961 			return EProcessed;			
       
   962 			}
       
   963 		}
       
   964 	aRequest->Complete(KErrNotFound);
       
   965 	return EProcessed;		
       
   966 	}
       
   967 
       
   968 /**
       
   969  Start Opening the database using the CActiveLoop class. Opening a file
       
   970  is a long running operation and is done in steps allowing other active objects
       
   971  processor time between each opening step.
       
   972 */
       
   973 void CStateOpening::InitialStep()
       
   974 	{
       
   975 	iActive->Register(*this, ETrue);
       
   976 	}
       
   977 
       
   978 /** 
       
   979 Perform the next step in the opening operation 
       
   980 @see InitialStep for more details
       
   981 */
       
   982 TBool CStateOpening::DoStepL()
       
   983 	{
       
   984 	if (!iFileName)
       
   985 		{
       
   986 		User::Leave(KErrGeneral);
       
   987 		}
       
   988 
       
   989 #ifdef __VERBOSE_DEBUG__
       
   990 	RDebug::Print(_L("[CNTMODEL] ------------- Open Database Step -------------"));
       
   991 #endif
       
   992 
       
   993 #if defined(__PROFILE_DEBUG__)
       
   994 	RDebug::Print(_L("[CNTMODEL] MTD:CStateOpening::DoStepL"));
       
   995 #endif 
       
   996 		
       
   997     iCntFile->OpenL(*iFileName);		
       
   998     // always go to this code if using SQLite
       
   999 	#ifdef __VERBOSE_DEBUG__
       
  1000   		RDebug::Print(_L("[CNTMODEL] ------------- Database Opened -------------"));
       
  1001 	#endif
       
  1002 		// The file is now open.
       
  1003 		DoCompletion(KErrNone);	
       
  1004 		iStateMachine.SetCurrentStateL(iStateMachine.StateWritable());
       
  1005 		// Check if Backup/Restore Agent indicates backup in progress (i.e. we
       
  1006 		// are opening database after backup starts).
       
  1007 		if (iStateMachine.DbManager().BackupRestoreAgent().BackupInProgress())
       
  1008 			{
       
  1009 			TContactDbObserverEvent event;
       
  1010 			event.iType = EContactDbObserverEventBackupBeginning;
       
  1011 			event.iContactId = 0;
       
  1012 			event.iConnectionId = 0;
       
  1013 			// The HandleBackupRestoreEventL() method of the CCntDbManager that
       
  1014 			// owns this state machine is called to send the appropriate request
       
  1015 			// to the state machine and to notify any observers.  The request
       
  1016 			// causes a transition from CStateWritable to CStateBackupRestore.
       
  1017 			// We need to send this event here since the event has only been
       
  1018 			// heard by CCntDbManager instances (and any of its observers) that
       
  1019 			// existed at the time when the backup first started.
       
  1020 			iStateMachine.DbManager().HandleBackupRestoreEventL(event);
       
  1021 			}
       
  1022 		return EFalse;
       
  1023 	}
       
  1024 	
       
  1025 /** 
       
  1026  Complete all Opening requests. More than one client (or internal 
       
  1027  request) may open a database file simultaneously. 
       
  1028  DoCompletion completes all outstanding Open requests.
       
  1029  
       
  1030  @param aError The error code used to complete the request.
       
  1031  */
       
  1032 void CStateOpening::DoCompletion(TInt aError)
       
  1033 	{
       
  1034 	TInt max = iOpenReqsStore.Count();
       
  1035 	for (TInt ii = 0; ii < max; ++ii)
       
  1036 		{
       
  1037 		iOpenReqsStore[ii]->Complete(aError);
       
  1038 		}
       
  1039 	iOpenReqsStore.ResetAndDestroy();
       
  1040 	}
       
  1041 
       
  1042 /**
       
  1043  Completes all open requests with an error code	
       
  1044 
       
  1045  @param aError The error code used to complete the request. 
       
  1046 */ 
       
  1047 void CStateOpening::DoError(TInt aError)
       
  1048 	{
       
  1049 	
       
  1050 	DEBUG_PRINT2(__VERBOSE_DEBUG__,_L("[CNTMODEL] ------------- Database Open Error %d -------------"), aError);
       
  1051 
       
  1052 	DoCompletion(aError);
       
  1053 	TRAP_IGNORE(iStateMachine.SetCurrentStateL(iStateMachine.StateClosed() ) );
       
  1054 	}
       
  1055 
       
  1056 /**
       
  1057  Sets the name of the file that is being opened. The name is held by
       
  1058  the opeing state and re-used when database needs to be closed and 
       
  1059  re-opened (for example during recovery)
       
  1060  
       
  1061  @param aFileName The name of the file that is being opened
       
  1062 */ 
       
  1063 void CStateOpening::SetFileNameL(const TDes& aFileName)
       
  1064 	{
       
  1065 	HBufC* tmpFileName  = HBufC::NewL(aFileName.Length());
       
  1066 	*tmpFileName = aFileName;
       
  1067 	delete iFileName;
       
  1068 	iFileName = tmpFileName;
       
  1069 	}
       
  1070 
       
  1071 /**
       
  1072  Once a Backup or Restore has completed we can re-open the database.
       
  1073  
       
  1074  @param aRequest Notification of Backup/Restore end request object  
       
  1075  @return EOwnershipPassed 
       
  1076  @see CStateOpening::OpenDatabaseFileL
       
  1077 */ 
       
  1078 TAccept CStateOpening::AcceptRequestL(CReqBackupRestoreEnd* aRequest) 
       
  1079 	{
       
  1080 	return OpenDatabaseFileL(aRequest);
       
  1081 	}
       
  1082 
       
  1083 /** 
       
  1084  Overridden read-only operations from base class: can't read from the database
       
  1085  while in the Opening state so defer these requests.
       
  1086  
       
  1087  @param aRequest Read contact item request object  
       
  1088  @return EDeferred if the request is not processed, EProcessed if the request is
       
  1089          completed with timeout error.
       
  1090  @see CState::DeferRequest 
       
  1091 */
       
  1092 TAccept CStateOpening::AcceptRequestL(CReqReadCnt* aRequest)
       
  1093 	{
       
  1094 	return DeferRequest(aRequest); // Uses the requests default timeout error
       
  1095 	}
       
  1096 
       
  1097 // @see CStateOpening::AcceptRequestL(CReqReadCnt* )	
       
  1098 TAccept CStateOpening::AcceptRequestL(CReqOpenCnt* aRequest)
       
  1099 	{
       
  1100 	return DeferRequest(aRequest);
       
  1101 	}
       
  1102 
       
  1103 
       
  1104 
       
  1105 // CStateWritable Class implementation //
       
  1106 
       
  1107 /** 
       
  1108  CStateWritable Class NewL factory constructor
       
  1109  @see CState constructor
       
  1110 */ 
       
  1111 CStateWritable* CStateWritable::NewL(CCntStateMachine& aStateMachine, CPersistenceLayer& aPersistenceLayer)
       
  1112 	{
       
  1113 	CStateWritable* stateWritable = new (ELeave) CStateWritable(aStateMachine, aPersistenceLayer);
       
  1114 	return stateWritable;
       
  1115 	}
       
  1116 
       
  1117 /** 
       
  1118  CStateWritable Class constructor
       
  1119  @see CState constructor
       
  1120 */ 
       
  1121 CStateWritable::CStateWritable(CCntStateMachine& aStateMachine, CPersistenceLayer& aPersistenceLayer)
       
  1122 :CState(aStateMachine, aPersistenceLayer)
       
  1123 	{
       
  1124 	}
       
  1125 
       
  1126 /** 
       
  1127  CStateWritable Class destructor
       
  1128  @see CState destructor
       
  1129 */ 
       
  1130 CStateWritable::~CStateWritable()
       
  1131 	{
       
  1132 	}
       
  1133 
       
  1134 
       
  1135 // Derived AcceptRequestL methods - Vistor Pattern
       
  1136 	
       
  1137 /**
       
  1138  Update a contact item in the database
       
  1139 
       
  1140  @param aRequest Update contact item request object  
       
  1141  @return TAccept EProcessed if finished processing request or one of the 
       
  1142          values returned by DeferWithTimeOutError
       
  1143  @see CState::DeferWithTimeOutError 		 	     
       
  1144 */ 	
       
  1145 TAccept CStateWritable::AcceptRequestL(CReqUpdateCnt* aRequest)
       
  1146 	{
       
  1147   	if (iStateMachine.LowDisk())
       
  1148   		{
       
  1149   		aRequest->Complete(KErrDiskFull);
       
  1150   		return EProcessed;
       
  1151   		}
       
  1152   	// Check if the contact has been locked 
       
  1153   	if (iStateMachine.TransactionLockL().IsLocked(aRequest->Item().Id()))
       
  1154   		{
       
  1155   		// If the request can not be procesed after the timeout period, it should 
       
  1156   		// complete with KErrInUse to assure binary compatibility with the original model
       
  1157 		return DeferWithTimeOutError(aRequest);
       
  1158   		}
       
  1159   
       
  1160   	TRAPD(updateErr,
       
  1161   		{
       
  1162 		TransactionStartLC(aRequest->SessionId());
       
  1163 		
       
  1164 		iPersistenceLayer.PersistenceBroker().SetConnectionId(aRequest->SessionId());
       
  1165 		iPersistenceLayer.PersistenceBroker().UpdateL(aRequest->Item(), aRequest->SessionId());
       
  1166 	
       
  1167 		TransactionCommitLP();
       
  1168   		});
       
  1169 	if (updateErr == KSqlErrGeneral)
       
  1170 		{
       
  1171 		// Write operation failed, probably due to view read activity
       
  1172 		return DeferWithTimeOutError(aRequest);
       
  1173 		}
       
  1174 	else if (updateErr != KErrNone)
       
  1175 		{
       
  1176 		// Unknown error, propagate the leave to the client
       
  1177 		User::Leave(updateErr);
       
  1178 		}
       
  1179 
       
  1180 	aRequest->Complete();
       
  1181 	return EProcessed;		
       
  1182 	}		
       
  1183 
       
  1184 /**
       
  1185  Commit an opened contact item to the database  
       
  1186  Unlock the contact item so other session can modify it.
       
  1187  Starts and commits a local transaction inorder to handle leaves.
       
  1188  
       
  1189  @param aRequest Commit contact item request object  
       
  1190  @return TAccept EProcessed if finished processing request or one of the 
       
  1191          values returned by DeferWithTimeOutError
       
  1192  @see CState::DeferWithTimeOutError 		 	     
       
  1193 */
       
  1194 TAccept CStateWritable::AcceptRequestL(CReqCommitCnt* aRequest)
       
  1195 	{
       
  1196   	if (iStateMachine.LowDisk())
       
  1197   		{
       
  1198   		aRequest->Complete(KErrDiskFull);
       
  1199   		return EProcessed;
       
  1200   		}
       
  1201 
       
  1202 	// Check if the contact has been locked by this session
       
  1203   	if (iStateMachine.TransactionLockL().IsLocked(aRequest->SessionId(), aRequest->Item().Id()))
       
  1204   		{
       
  1205   		// If the request can not be procesed after the timeout period, it should 
       
  1206   		// complete with KErrInUse to assure binary compatibility with the original model
       
  1207 		return DeferWithTimeOutError(aRequest);
       
  1208   		}
       
  1209 	
       
  1210   	TRAPD(commitErr,
       
  1211   		{
       
  1212 		TransactionStartLC(aRequest->SessionId());
       
  1213 	    iPersistenceLayer.PersistenceBroker().SetConnectionId(aRequest->SessionId());
       
  1214 		iPersistenceLayer.PersistenceBroker().UpdateL(aRequest->Item(), aRequest->SessionId());
       
  1215 		User::LeaveIfError(iStateMachine.TransactionLockL().UnLockL(aRequest->SessionId(), aRequest->Item().Id()));
       
  1216 		TransactionCommitLP();
       
  1217 		});
       
  1218   	if (commitErr == KSqlErrGeneral)
       
  1219   		{
       
  1220   		// Can't commit contact, probably due to idle sorter activity
       
  1221   		return DeferWithTimeOutError(aRequest);
       
  1222   		}
       
  1223   	else
       
  1224   		{
       
  1225   		User::LeaveIfError(commitErr);
       
  1226   		}
       
  1227   	
       
  1228 	aRequest->Complete();
       
  1229 	return EProcessed;		
       
  1230 	}
       
  1231 
       
  1232 
       
  1233 /**
       
  1234  Delete a contact item from the database. 
       
  1235  Starts and commits a local transaction inorder to handle leaves.
       
  1236  Checks if the contact item is locked by another session.
       
  1237  
       
  1238  @param aRequest Delete contact item request object  
       
  1239  @return TAccept EProcessed if finished processing request or one of the 
       
  1240          values returned by DeferWithTimeOutError
       
  1241  @see CState::DeferWithTimeOutError
       
  1242 */
       
  1243 TAccept CStateWritable::AcceptRequestL(CReqDeleteCnt* aRequest)
       
  1244 	{
       
  1245 	// Check if the contact has been locked 
       
  1246 	if (iStateMachine.TransactionLockL().IsLocked(aRequest->CntItemId()))
       
  1247   		{
       
  1248   		// If the request can not be procesed after the timeout period, it should 
       
  1249   		// complete with KErrInUse - the contact is locked 
       
  1250 		return DeferWithTimeOutError(aRequest);
       
  1251 		}	
       
  1252 	
       
  1253 	TRAPD(deleteErr,
       
  1254 		{
       
  1255 		TransactionStartLC(aRequest->SessionId());
       
  1256 	
       
  1257 		iPersistenceLayer.PersistenceBroker().SetConnectionId(aRequest->SessionId());
       
  1258 		CContactItem* cntItem = iPersistenceLayer.PersistenceBroker().DeleteLC(aRequest->CntItemId(), aRequest->SessionId(), ESendEvent);
       
  1259 		CleanupStack::PopAndDestroy(cntItem);
       
  1260 	
       
  1261 		TransactionCommitLP();
       
  1262 		});
       
  1263 	if (deleteErr == KSqlErrGeneral)
       
  1264 		{
       
  1265 		// Write operation failed, probably due to view read activity
       
  1266 		return DeferWithTimeOutError(aRequest);
       
  1267 		}
       
  1268 	else if (deleteErr != KErrNone)
       
  1269 		{
       
  1270 		// Unknown error, propagate the leave to the client
       
  1271 		User::Leave(deleteErr);
       
  1272 		}
       
  1273 		
       
  1274 	aRequest->Complete();
       
  1275 	return EProcessed;		
       
  1276 	}		
       
  1277 	
       
  1278 /**
       
  1279  Create a new contact item
       
  1280 
       
  1281  @param aRequest Create contact item request object  
       
  1282  @return TAccept EProcessed 
       
  1283 */
       
  1284 TAccept CStateWritable::AcceptRequestL(CReqCreateCnt* aRequest)
       
  1285 	{
       
  1286   	if (iStateMachine.LowDisk())
       
  1287   		{
       
  1288   		aRequest->Complete(KErrDiskFull);
       
  1289   		return EProcessed;
       
  1290   		}
       
  1291 	
       
  1292   	TContactItemId cntID(KNullContactId);
       
  1293   	TRAPD(createErr,
       
  1294   		{
       
  1295 	  	TransactionStartLC(aRequest->SessionId());
       
  1296 	    
       
  1297 		iPersistenceLayer.PersistenceBroker().SetConnectionId(aRequest->SessionId());
       
  1298 	    cntID = iPersistenceLayer.PersistenceBroker().CreateL(aRequest->Item(), aRequest->SessionId());
       
  1299 		
       
  1300 	    TransactionCommitLP();
       
  1301   		});
       
  1302 	if (createErr == KSqlErrGeneral)
       
  1303 		{
       
  1304 		// Write operation failed, probably due to view read activity
       
  1305 		return DeferWithTimeOutError(aRequest);
       
  1306 		}
       
  1307 	else if (createErr != KErrNone)
       
  1308 		{
       
  1309 		// Unknown error, propagate the leave to the client
       
  1310 		User::Leave(createErr);
       
  1311 		}
       
  1312 	
       
  1313 	aRequest->Complete(cntID);
       
  1314 	return EProcessed;		
       
  1315 	}		
       
  1316 
       
  1317 /**
       
  1318  Change the current state of the model to CStateTablesClosed. 
       
  1319  The database is unavailable.		
       
  1320   
       
  1321  @param aRequest Close database tables request object  
       
  1322  @return TAccept EProcessed if finished processing request
       
  1323 */   
       
  1324 TAccept CStateWritable::AcceptRequestL(CReqCloseTables* aRequest)
       
  1325   	{
       
  1326   	iPersistenceLayer.ContactsFileL().CloseTablesL(ETrue);	
       
  1327   	iStateMachine.SetCurrentStateL(iStateMachine.StateTablesClosed());
       
  1328   	aRequest->Complete();
       
  1329   	return EProcessed;		
       
  1330   	}
       
  1331 
       
  1332 
       
  1333 /** 
       
  1334  Do nothing for Cancel - it's too late as the database is already open.
       
  1335 
       
  1336  @param aRequest Cancel open database request object  
       
  1337  @return TAccept EProcessed if finished processing request
       
  1338 */ 
       
  1339 TAccept CStateWritable::AcceptRequestL(CReqCancelAsyncOpen* aRequest)
       
  1340 	{
       
  1341 	aRequest->Complete();
       
  1342 	return EProcessed;		
       
  1343 	}
       
  1344 
       
  1345 
       
  1346 /**
       
  1347  Start a database transaction by moving to a transaction state		
       
  1348  
       
  1349  @param aRequest Begin transaction request object  
       
  1350  @return TAccept EProcessed if finished processing request
       
  1351  		 	     EDeferred if the request was not processed 
       
  1352 */ 
       
  1353 TAccept CStateWritable::AcceptRequestL(CReqDbBeginTrans* aRequest)
       
  1354 	{
       
  1355   	// In the current implementation there are no operations allowed under the
       
  1356   	// low disk condition that will reduce the size of the database: this is in
       
  1357   	// line with Contacts Model 1 behaviour.  Later, when we allow operations
       
  1358   	// that reduce the size of the database, this check should be removed and
       
  1359   	// allow the transition to CStateTransaction.
       
  1360   	if (iStateMachine.LowDisk())
       
  1361   		{
       
  1362   		aRequest->Complete(KErrDiskFull);
       
  1363   		return EProcessed;
       
  1364   		}	
       
  1365 	
       
  1366 	iStateMachine.SetCurrentStateL(iStateMachine.StateTransaction());
       
  1367 	return iStateMachine.CurrentState().AcceptRequestL(aRequest);
       
  1368 	}
       
  1369 		
       
  1370 /**
       
  1371  Start a database backup/restore by moving to CStateBackupRestore state.  If any
       
  1372  contact items are locked or there is any asynchronous activity using the
       
  1373  database then we cannot close the file (and the backup/restore client will not
       
  1374  be allowed to backup/restore).
       
  1375 
       
  1376  @param aRequest Notification of backup/restore begin request object  
       
  1377  @return TAccept EProcessed if finished processing request
       
  1378 */ 
       
  1379 TAccept CStateWritable::AcceptRequestL(CReqBackupRestoreBegin* aRequest)
       
  1380 	{
       
  1381 	if (!iStateMachine.TransactionLockL().AnyLocked() &&
       
  1382 		!iStateMachine.AsyncActivity())
       
  1383 		{
       
  1384 		// First reset collection, since it construct views based on table 
       
  1385 		// Reset will fail if called after closing tables 
       
  1386 		iPersistenceLayer.FactoryL().GetCollectorL().Reset(); 
       
  1387 		// Close the file to allow the backup/restore to take place.	
       
  1388 		iPersistenceLayer.ContactsFileL().Close();
       
  1389 		}
       
  1390 	iStateMachine.SetCurrentStateL(iStateMachine.StateBackupRestore());
       
  1391 	aRequest->Complete();
       
  1392 	return EProcessed;
       
  1393 	}
       
  1394 
       
  1395 
       
  1396 /**
       
  1397  Reset the speed dials		
       
  1398 
       
  1399  @param aRequest Reset speed dials request object  
       
  1400  @return TAccept EProcessed if finished processing request
       
  1401  		 	     EDeferred if the request was not processed 
       
  1402 */ 
       
  1403 TAccept CStateWritable::AcceptRequestL(CReqSetSpeedDial* aRequest)
       
  1404 	{
       
  1405 	if (iStateMachine.LowDisk())
       
  1406   		{
       
  1407   		aRequest->Complete(KErrDiskFull);
       
  1408   		return EProcessed;
       
  1409   		}
       
  1410 	
       
  1411 	TContactItemId contactId = aRequest->TheContactId();
       
  1412 	if(contactId == 0)
       
  1413 		{
       
  1414 		User::Leave(KErrArgument);
       
  1415 		}
       
  1416 
       
  1417 	// Obtain the contact ID currently associated with the speed dial index.
       
  1418 	// The phone number is not being used at the moment
       
  1419 	TSpeedDialPhoneNumber phoneNumberFromSpeedDialTable;
       
  1420 	TContactItemId OldContactId = aRequest->SpeedDialTable().SpeedDialContactItem(aRequest->SpeedDialIndex(), phoneNumberFromSpeedDialTable);
       
  1421 	
       
  1422 	// We should not be able to remove a speed dial from an open item, even if
       
  1423 	// it has been opened by the same session: use the IsLocked() method which
       
  1424 	// ignores session ID.
       
  1425     TBool isLocked = iStateMachine.TransactionLockL().IsLocked(OldContactId);		
       
  1426 				
       
  1427 	// This code resets an entry from the speed dial table, as required when calling either 
       
  1428 	// CContactDatabase::ResetServerSpeedDialsL() (contactId is KNullContactId) or 
       
  1429 	// CContactDatabase::RemoveSpeedDialFieldL()  (contactId must be equal to the OldContactId)
       
  1430 	// If the field index is -1, it indicates that the speed dial entry corresponding 
       
  1431 	// to the speed dial index passed in the request should be reset.	
       
  1432 	TBool doResetOldContactItem = ETrue;
       
  1433 	TBool doResetSpeedDialEntry = aRequest->TheFieldIndex() == -1;			
       
  1434 	
       
  1435 	if (doResetSpeedDialEntry)
       
  1436 		{		
       
  1437 		if (contactId == KNullContactId || contactId == OldContactId)
       
  1438 		    {
       
  1439 		    if (isLocked)
       
  1440 		        {
       
  1441 		        User::Leave(KErrInUse);
       
  1442 		        }			    
       
  1443 		        
       
  1444 		    aRequest->IniFileManager().SetSpeedDialIdForPositionL(aRequest->SpeedDialIndex(), KNullContactId, KNullDesC(), aRequest->SessionId(), EFalse);				    
       
  1445 		    }
       
  1446 		else
       
  1447 		    {
       
  1448 		    doResetOldContactItem = EFalse;
       
  1449 		    }
       
  1450 		}
       
  1451 
       
  1452 	//Everything, i.e. removal of the old speed dial reference and 
       
  1453 	//the setting of the new takes place during the same transaction
       
  1454 	//i.e. start transaction here
       
  1455 	TransactionStartLC(aRequest->SessionId());
       
  1456 		{
       
  1457 		// Check if there is already a contact associated with this speed dial
       
  1458 		// index.
       
  1459 		if (OldContactId != KErrNotFound && doResetOldContactItem)
       
  1460 			{
       
  1461 			// Fetch the item from the ID, remember to pop it
       
  1462 			CContactItem* cntItem = iPersistenceLayer.PersistenceBroker().ReadLC(OldContactId, aRequest->ItemViewDef(), EPlAllInfo, aRequest->SessionId());	
       
  1463 			// Remove speed dial attributes from the contact item field.
       
  1464 			TUid fieldTypeUid = CCntServerSpeedDialManager::SpeedDialFieldUidFromSpeedDialPosition(aRequest->SpeedDialIndex());
       
  1465 			TInt fieldIdFound = cntItem->CardFields().Find(fieldTypeUid);
       
  1466 			if (fieldIdFound != KErrNotFound)
       
  1467 				{		
       
  1468 				cntItem->CardFields()[fieldIdFound].RemoveFieldType(fieldTypeUid);
       
  1469 				cntItem->CardFields()[fieldIdFound].SetSpeedDial(EFalse);
       
  1470 				}
       
  1471 			// Update changes to the contact item in the database.
       
  1472 			iPersistenceLayer.PersistenceBroker().SetConnectionId(aRequest->SessionId());
       
  1473 			iPersistenceLayer.PersistenceBroker().UpdateL(*cntItem, aRequest->SessionId(), ETrue);
       
  1474 			CleanupStack::PopAndDestroy(cntItem);
       
  1475 			}
       
  1476 		
       
  1477 		if (!doResetSpeedDialEntry)
       
  1478 			{
       
  1479 			// Fetch the contact item containing the phone number to be used as
       
  1480 			// a speed dial.
       
  1481 			CContactItem* cntItem = iPersistenceLayer.PersistenceBroker().ReadLC(contactId, aRequest->ItemViewDef(), EPlAllInfo, aRequest->SessionId());	
       
  1482 			if (cntItem->CardFields().Count() < 1)
       
  1483 				{
       
  1484 				User::Leave(KErrUnknown);		
       
  1485 				}	
       
  1486 			// Get the field containing the number to be associated with the
       
  1487 			// speed dial.
       
  1488 			CContactItemField& speeddialField = cntItem->CardFields()[aRequest->TheFieldIndex()];
       
  1489 			// Add speed dial attributes to the contact item field.
       
  1490 			TUid fieldTypeUid = CCntServerSpeedDialManager::SpeedDialFieldUidFromSpeedDialPosition(aRequest->SpeedDialIndex());
       
  1491 			if (!speeddialField.ContentType().ContainsFieldType(fieldTypeUid))
       
  1492 				{
       
  1493 				speeddialField.AddFieldTypeL(fieldTypeUid);
       
  1494 				}
       
  1495 			speeddialField.SetUserAddedField(ETrue);
       
  1496 			speeddialField.SetSpeedDial(ETrue);
       
  1497 			// Get the phone number from the field.
       
  1498 			if (speeddialField.StorageType() != KStorageTypeText)
       
  1499 				{
       
  1500 				User::Leave(KErrArgument);		
       
  1501 				}	
       
  1502 			// Truncate it if its length is > KSpeedDialPhoneLength
       
  1503 			TInt numLen = Min(speeddialField.TextStorage()->Text().Length(), KSpeedDialPhoneLength);
       
  1504 			TPtrC phoneNumber(speeddialField.TextStorage()->Text().Mid(0, numLen));
       
  1505 			// Update changes to the contact item in the database.
       
  1506 			iPersistenceLayer.PersistenceBroker().SetConnectionId(aRequest->SessionId());
       
  1507 			// Update the speed dial table.
       
  1508 			aRequest->IniFileManager().SetSpeedDialIdForPositionL(aRequest->SpeedDialIndex(), contactId, phoneNumber, aRequest->SessionId(), EFalse);
       
  1509 			iPersistenceLayer.PersistenceBroker().UpdateL(*cntItem, aRequest->SessionId(), ETrue);
       
  1510 			// Unlock the item.
       
  1511 			User::LeaveIfError(iStateMachine.TransactionLockL().UnLockL(aRequest->SessionId(), contactId));
       
  1512 			CleanupStack::PopAndDestroy(cntItem);
       
  1513 			}
       
  1514 		}
       
  1515 	TransactionCommitLP();
       
  1516 	aRequest->Complete();
       
  1517 	return EProcessed;		
       
  1518 	}
       
  1519 
       
  1520 
       
  1521 /**
       
  1522  Set own card data	
       
  1523 
       
  1524  @param aRequest Set own card request object  
       
  1525  @return TAccept EProcessed if finished processing request
       
  1526  		 	     EDeferred if the request was not processed 
       
  1527 */ 		
       
  1528 TAccept CStateWritable::AcceptRequestL(CReqSetOwnCard* aRequest)
       
  1529 	{
       
  1530 	if (iStateMachine.LowDisk())
       
  1531   		{
       
  1532   		aRequest->Complete(KErrDiskFull);
       
  1533   		return EProcessed;
       
  1534   		}
       
  1535 
       
  1536 	TUid aContactType = aRequest->Item().Type();
       
  1537 
       
  1538 	// this should leave with kerrnotsupported if the type doesn't match!!!!
       
  1539 	if (aContactType==KUidContactGroup || aContactType==KUidContactTemplate || aContactType==KUidContactCardTemplate)
       
  1540 		{
       
  1541 		User::Leave(KErrNotSupported);	
       
  1542 		}
       
  1543 	// if the requested item is already set as own card then just return
       
  1544 	if (aRequest->Item().Id()==iPersistenceLayer.ContactProperties().OwnCardIdL())
       
  1545 		{
       
  1546 		aRequest->Complete();
       
  1547 		return EProcessed;	
       
  1548 		}
       
  1549 		
       
  1550 	// if the requested ID is system template or KNUllContactID then leave
       
  1551 	if (aRequest->Item().Id()==KGoldenTemplateId || aRequest->Item().Id()==KNullContactId)
       
  1552 		{
       
  1553 		User::Leave(KErrNotFound);
       
  1554 		}
       
  1555 	//Everything, i.e. removal of the old own card reference and 
       
  1556 	//the setting of the new takes place during the same transaction
       
  1557 	//i.e. start transaction here
       
  1558 	TransactionStartLC(aRequest->SessionId());
       
  1559 		{//reset the old own card to become an ordinary contacts card
       
  1560 		if (iPersistenceLayer.ContactProperties().OwnCardIdL() != KNullContactId)
       
  1561 			{
       
  1562 			// Fetch the old current item from the ID, remember to pop it
       
  1563 			CContactItem* oldOwnCard = iPersistenceLayer.PersistenceBroker().ReadLC(iPersistenceLayer.ContactProperties().OwnCardIdL(), aRequest->ItemViewDef(), EPlAllInfo, aRequest->SessionId());	
       
  1564 			iPersistenceLayer.PersistenceBroker().SetConnectionId(aRequest->SessionId());
       
  1565 			//change the type to KUidContactCard for the old ownCard
       
  1566 			iPersistenceLayer.PersistenceBroker().ChangeTypeL(oldOwnCard->Id(), KUidContactCard);
       
  1567 			CleanupStack::PopAndDestroy(oldOwnCard);
       
  1568 			}			
       
  1569 		iPersistenceLayer.PersistenceBroker().SetConnectionId(aRequest->SessionId());
       
  1570 		//Now set the new own card
       
  1571 		iPersistenceLayer.PersistenceBroker().ChangeTypeL(aRequest->Item().Id(), KUidContactOwnCard);
       
  1572 		}
       
  1573 	TransactionCommitLP();
       
  1574 	aRequest->Complete();
       
  1575 	return EProcessed;		
       
  1576 	}		
       
  1577 
       
  1578 /** 
       
  1579  The error code which deferred requests use after a timeout from the 
       
  1580  writable state  this maintians consistency with the original contacts model
       
  1581  
       
  1582  @return The most common the timeout error code -KErrInUse- used in the writable state
       
  1583 */ 
       
  1584 TInt CStateWritable::TimeOutErrorCode()
       
  1585 	{
       
  1586 	return KErrInUse;		
       
  1587 	}
       
  1588 
       
  1589 
       
  1590 
       
  1591 // CStateTransaction Implementation//
       
  1592 /** 
       
  1593  CStateTransaction Class NewL factory constructor
       
  1594  Create a transaction state
       
  1595  @see CState constructor
       
  1596 */ 
       
  1597 CStateTransaction* CStateTransaction::NewL(CCntStateMachine& aStateMachine, CPersistenceLayer& aPersistenceLayer)
       
  1598 	{
       
  1599 	CStateTransaction* stateTransaction = new (ELeave) CStateTransaction(aStateMachine, aPersistenceLayer);
       
  1600 	CleanupStack::PushL(stateTransaction);
       
  1601 	stateTransaction->ConstructL();
       
  1602 	CleanupStack::Pop(stateTransaction);
       
  1603 	return stateTransaction;
       
  1604 	}
       
  1605 
       
  1606 /**
       
  1607  Instantiate the CTransctionTimer object. The transaction state contains a timer
       
  1608  because a client may put the server into a transation state and for some unknown 
       
  1609  reason my never complete the transaction. Such a senario would result in the server
       
  1610  becoming unusable (ie No other client can modify the database until the transaction 
       
  1611  is completed or rolled back). If the CStateTransaction class does not hear from the
       
  1612  client for a given time, the transaction times out and is rolled back. This is the 
       
  1613  responsibility of the CTransactionTimer.
       
  1614 */
       
  1615 void CStateTransaction::ConstructL()
       
  1616 	{
       
  1617 	// Pass the transaction state that will timeout.
       
  1618 	iTimeOut = CTransactionTimer::NewL(*this); 
       
  1619 	}
       
  1620 
       
  1621 /** 
       
  1622  CStateTransaction Class constructor
       
  1623  @see CState constructor
       
  1624 */ 
       
  1625 CStateTransaction::CStateTransaction(CCntStateMachine& aStateMachine, CPersistenceLayer& aPersistenceLayer)
       
  1626 : CState(aStateMachine, aPersistenceLayer), iEventQ(KCntEventGranularity)
       
  1627 	{
       
  1628 	}
       
  1629 
       
  1630 /** 
       
  1631  CStateTransaction Class constructor
       
  1632  @see CState constructor
       
  1633 */ 
       
  1634 CStateTransaction::~CStateTransaction()
       
  1635 	{
       
  1636 	delete iTimeOut;
       
  1637 	iEventQ.Close();
       
  1638 	}
       
  1639 	
       
  1640 /**	
       
  1641  Cancel the transaction - will result in a database rollback
       
  1642  implemented in the base CState class
       
  1643  This overwritten CancelTransaction is only ever called when the transaction
       
  1644  times out. The base class CState::CancelTransactionL() is called when any 
       
  1645  state class or persistence layer method leaves
       
  1646 */
       
  1647 void CStateTransaction::CancelTransactionL()
       
  1648   	{
       
  1649   	CState::RollbackTransAndRecoverL(EFalse); 
       
  1650   	iSessionId = 0; // Allow another session enter a transaction state.
       
  1651   	}
       
  1652 
       
  1653 /**	
       
  1654  Get the CStateTransaction default timeout error code.
       
  1655  
       
  1656  @return TInt ErrLocked
       
  1657 */
       
  1658 TInt CStateTransaction::TimeOutErrorCode()
       
  1659 	{
       
  1660 	return KErrLocked;	
       
  1661 	}
       
  1662 
       
  1663 
       
  1664 /**
       
  1665  Begin a database transaction, start the transaction timeout which rolls-back
       
  1666  the transaction if the session dies. Reset the transactions event queue - this
       
  1667  event-queue queues all events until the transaction has been committed.
       
  1668  
       
  1669  @param aRequest Begin a database transaction
       
  1670  @return TAccept EProcessed if finished processing request, or one of the values returned
       
  1671                  by CState::DeferRequest 
       
  1672  @see CState::DeferRequest 
       
  1673 */ 
       
  1674 TAccept CStateTransaction::AcceptRequestL(CReqDbBeginTrans* aRequest)
       
  1675 	{
       
  1676 	
       
  1677 	#if defined(__PROFILE_DEBUG__)
       
  1678 		RDebug::Print(_L("[CNTMODEL] MTD: CStateTransaction::AcceptRequestL"));
       
  1679 	#endif 
       
  1680 	
       
  1681 	// Only one session can ever be in a transaction state
       
  1682 	if (iSessionId == 0)
       
  1683 		{
       
  1684 		iSessionId = aRequest->SessionId();
       
  1685 		iPersistenceLayer.TransactionManager().StartTransactionL();
       
  1686 		iTimeOut->Start();
       
  1687 		aRequest->Complete();
       
  1688 		// Reset the event queue - although it is also reset when a transaction
       
  1689 		// is committed or explicitly rolled back, it will now be reset if a rollback occured 
       
  1690 		// because of a leave. Resetting the queue after commit/explicit rollback free's memory.
       
  1691 		iEventQ.Reset(); 
       
  1692 		return EProcessed;	
       
  1693 		}
       
  1694 	// This session has already started a transaction 	
       
  1695 	if (iSessionId == aRequest->SessionId())
       
  1696 		{
       
  1697 		aRequest->Complete();
       
  1698 		return EProcessed;	
       
  1699 		}
       
  1700 	// Another session has started a transaction
       
  1701 	return DeferRequest(aRequest); 
       
  1702 	}
       
  1703 
       
  1704 
       
  1705 /**
       
  1706  Commit the current transaction if started by the same session.
       
  1707  Stop the transaction timer.
       
  1708  Propogate all events to the observer.
       
  1709  Compress the database.
       
  1710  
       
  1711  @param aRequest Commit a database transaction
       
  1712  @return TAccept EProcessed if the transaction has been committed
       
  1713  @leave KErrLocked if a different session had started the transaction
       
  1714 */
       
  1715 TAccept CStateTransaction::AcceptRequestL(CReqDbCommitTrans* aRequest)
       
  1716 	{
       
  1717  	if (iSessionId == aRequest->SessionId())
       
  1718  		{
       
  1719 		TRAPD(commitErr, iPersistenceLayer.TransactionManager().CommitCurrentTransactionL(aRequest->SessionId()));
       
  1720 		if (commitErr == KSqlErrGeneral)
       
  1721 			{
       
  1722 			// Operation has probably been blocked due to read lock by view idle sorter.
       
  1723 			return DeferWithTimeOutError(aRequest);
       
  1724 			}
       
  1725 		else
       
  1726 			{
       
  1727 			User::LeaveIfError(commitErr);
       
  1728 			}
       
  1729 		
       
  1730 		iTimeOut->Stop(); // Transaction completed - shouldn't timeout
       
  1731 
       
  1732 		// The database had been updated. All session should now be notified
       
  1733 		// of events.
       
  1734 		PropagateDatabaseEventsL();
       
  1735 		
       
  1736 		iStateMachine.SetCurrentStateL(iStateMachine.StateWritable());				
       
  1737 
       
  1738 		iSessionId = 0;
       
  1739 		// Only complete the request after the last leaving method. If 
       
  1740 		// a leave occurs after the request (message) has been completed, then the method 
       
  1741 		// CCntSession::ServiceError will try to complete the message a second time
       
  1742 		// causing a panic. 
       
  1743 		aRequest->Complete();
       
  1744  		}
       
  1745  	else
       
  1746 		{
       
  1747 		StrayRequestL(aRequest); // Only the current session should be able to
       
  1748 		}						 // send a commit transaction request
       
  1749 	return EProcessed;		
       
  1750 	}
       
  1751 
       
  1752 
       
  1753 
       
  1754 /** 
       
  1755  Rollback the current transaction if started by the same session
       
  1756  Reset the event queue as no operation has been committed to the database.
       
  1757  Don't compress the database as it hasn't changed.
       
  1758  Notify the observer that a rollback has occured.
       
  1759  
       
  1760  @param aRequest Rollback a database transaction
       
  1761  @return TAccept EProcessed if the transaction has been rolled back
       
  1762  @leave KErrLocked if a different session had started the transaction
       
  1763 */
       
  1764 TAccept CStateTransaction::AcceptRequestL(CReqDbRollbackTrans* aRequest)
       
  1765 	{
       
  1766  	if (iSessionId == aRequest->SessionId())
       
  1767  		{
       
  1768 		iEventQ.Reset(); // Empty the event queue - no operations have been committed
       
  1769 						 // so sessions should never be notified of the event
       
  1770 
       
  1771  		iCurrentTransactionSessionId = aRequest->SessionId();
       
  1772 		CState::RollbackTransAndRecoverL(ETrue);
       
  1773 		iSessionId = 0;
       
  1774 
       
  1775 		// Transaction completed - shouldn't timeout
       
  1776 		iTimeOut->Stop(); 
       
  1777 	
       
  1778 		// Rollback event needs to be propagated.
       
  1779 		TContactDbObserverEvent event;
       
  1780 		event.iType			= EContactDbObserverEventRollback;
       
  1781 		event.iContactId	= KNullContactId;
       
  1782 		event.iConnectionId = iSessionId;
       
  1783 		iStateMachine.DbManager().HandleDatabaseEventL(event);
       
  1784 
       
  1785 		aRequest->Complete();
       
  1786  		}
       
  1787  	else
       
  1788 		{
       
  1789 		StrayRequestL(aRequest); // Only the current session should be able
       
  1790 		}						 // to rollback a transaction
       
  1791 	return EProcessed;
       
  1792 	}
       
  1793 
       
  1794 
       
  1795 /** 
       
  1796  A session tried to commit or rollback a transaction that was started
       
  1797  by another session. Destroy the request and leave.
       
  1798 
       
  1799  @param aRequest A pointer to a request object scoped to it parent class.
       
  1800  @leave KErrLocked A different session had started the transaction
       
  1801 */ 
       
  1802 void CStateTransaction::StrayRequestL(CCntRequest* /* aRequest */)
       
  1803 	{
       
  1804 	User::Leave(KErrLocked);	
       
  1805 	}
       
  1806 
       
  1807 
       
  1808 /**
       
  1809  Create a contact item from a the session that started the transaction
       
  1810  
       
  1811  @param aRequest The request that will contain the created contact item 
       
  1812  @return TAccept EProcessed if finished processing request
       
  1813  		 	     EDeferred if the request was not processed 
       
  1814 */
       
  1815 TAccept CStateTransaction::AcceptRequestL(CReqCreateCnt* aRequest)
       
  1816 	{
       
  1817 	#if defined(__PROFILE_DEBUG__)
       
  1818 		RDebug::Print(_L("[CNTMODEL] MTD: CStateTransaction::AcceptRequestL"));
       
  1819 	#endif 	
       
  1820 	
       
  1821   	if (iStateMachine.LowDisk())
       
  1822   		{
       
  1823   		aRequest->Complete(KErrDiskFull);
       
  1824   		return EProcessed;
       
  1825   		}
       
  1826 	
       
  1827  	if (iSessionId == aRequest->SessionId())
       
  1828  		{
       
  1829  		TContactItemId itemId(KNullContactId);
       
  1830  		TRAPD(createErr, itemId = iPersistenceLayer.PersistenceBroker().CreateL(aRequest->Item(),  aRequest->SessionId()));
       
  1831  		if (createErr == KSqlErrGeneral)
       
  1832  			{
       
  1833  			// Can't create contact item, probably due to view idle sorter activity
       
  1834  			return DeferWithTimeOutError(aRequest);
       
  1835  			}
       
  1836  		else
       
  1837  			{
       
  1838  			User::LeaveIfError(createErr);
       
  1839  			}
       
  1840  		
       
  1841 		aRequest->Complete(itemId);
       
  1842 		iTimeOut->Reset(); // restart the timeout as the client session is still alive
       
  1843 		return EProcessed;
       
  1844 		}
       
  1845  	
       
  1846  	// The session that is trying to perform this operation has not started the transaction
       
  1847  	return DeferWithTimeOutError(aRequest);
       
  1848 	}
       
  1849 
       
  1850 /**
       
  1851  Read a contact item - always allow read operation from any session 
       
  1852  ie the session does not need to have started the transaction to read the contact item
       
  1853  
       
  1854  @param aRequest The request that will contain the contact item read from the database
       
  1855  @return TAccept EProcessed if finished processing request
       
  1856  		 	     EDeferred if the request was not processed 
       
  1857 */ 
       
  1858 TAccept CStateTransaction::AcceptRequestL(CReqReadCnt* aRequest)
       
  1859 	{
       
  1860 	iTimeOut->Reset(); // Reset the timeout, the client is still alive
       
  1861 	return CState::AcceptRequestL(aRequest);
       
  1862 	}
       
  1863 	
       
  1864 /**
       
  1865  Update a contact item from a the session that started the transaction	
       
  1866  
       
  1867  @param aRequest The request that contain the contact item that is to be updated in the database
       
  1868  @return TAccept EProcessed if finished processing request, or one of the values returned
       
  1869                  by CState::DeferRequest 
       
  1870  @see CState::DeferRequest 
       
  1871 */ 
       
  1872 TAccept CStateTransaction::AcceptRequestL(CReqUpdateCnt* aRequest)
       
  1873 	{
       
  1874   	if (iStateMachine.LowDisk())
       
  1875   		{
       
  1876   		aRequest->Complete(KErrDiskFull);
       
  1877   		return EProcessed;
       
  1878   		}
       
  1879 
       
  1880 	// Check if the contact has been locked by any session - including this session
       
  1881 	// This is for reasons of compatibility with the original model only
       
  1882   	if (iStateMachine.TransactionLockL().IsLocked(aRequest->Item().Id()) == EFalse)
       
  1883   		{
       
  1884   		 if (iSessionId == aRequest->SessionId())
       
  1885 	 		{
       
  1886 	 		TRAPD(updateErr, iPersistenceLayer.PersistenceBroker().UpdateL(aRequest->Item(), aRequest->SessionId()));
       
  1887 			if (updateErr == KSqlErrGeneral)
       
  1888 				{
       
  1889 				// Can't update item, probably due to idle sorter activity
       
  1890 				return DeferWithTimeOutError(aRequest);
       
  1891 				}
       
  1892 			else
       
  1893 				{
       
  1894 				User::LeaveIfError(updateErr);
       
  1895 				}
       
  1896 			
       
  1897 			aRequest->Complete();
       
  1898 			iTimeOut->Reset(); 
       
  1899 			
       
  1900 			return EProcessed;
       
  1901 			}
       
  1902 	 	else
       
  1903 	 		{
       
  1904 	 		// The session that is trying to perform this operation has not started the transaction
       
  1905 	 		return DeferWithTimeOutError(aRequest);
       
  1906 	 		}
       
  1907   		}
       
  1908   	else
       
  1909   		{
       
  1910   		// If the request can not be procesed after the timeout period, it should 
       
  1911   		// complete with KErrInUse as the contact is locked
       
  1912   		aRequest->SetTimeOutError(KErrInUse);  		
       
  1913   		}
       
  1914 	return DeferRequest(aRequest);	
       
  1915 	}
       
  1916 	
       
  1917 /**
       
  1918  Delete a contact item if the delete request is from the same session that started the transaction
       
  1919  
       
  1920  @param aRequest The request that contain the contact item ID that is to be deleted from the database
       
  1921  @return TAccept EProcessed if finished processing request, or one of the values returned
       
  1922                  by CState::DeferRequest 
       
  1923  @see CState::DeferRequest 
       
  1924 */ 
       
  1925 TAccept CStateTransaction::AcceptRequestL(CReqDeleteCnt* aRequest)
       
  1926 	{
       
  1927 	if (iStateMachine.LowDisk())
       
  1928   		{
       
  1929   		aRequest->Complete(KErrDiskFull);
       
  1930   		return EProcessed;
       
  1931   		}	
       
  1932 	
       
  1933 	// Check if the contact has been locked by any session - including this session
       
  1934   	if (iStateMachine.TransactionLockL().IsLocked(aRequest->CntItemId()) == EFalse)
       
  1935   		{
       
  1936 	 	if (iSessionId == aRequest->SessionId())
       
  1937 	 		{
       
  1938 	 		CContactItem* item = NULL; 
       
  1939 	 		TRAPD(deleteErr, item = iPersistenceLayer.PersistenceBroker().DeleteLC(aRequest->CntItemId(), aRequest->SessionId(), aRequest->NotificationEventAction());CleanupStack::PopAndDestroy(item));
       
  1940 	 		if (deleteErr == KSqlErrGeneral)
       
  1941 	 			{
       
  1942 	 			// Delete failed, probably due to idle sorter activity
       
  1943 	 			return DeferWithTimeOutError(aRequest);
       
  1944 	 			}
       
  1945 	 		else
       
  1946 	 			{
       
  1947 	 			User::LeaveIfError(deleteErr);
       
  1948 	 			}
       
  1949 	 		
       
  1950 			aRequest->Complete();
       
  1951 			iTimeOut->Reset(); 
       
  1952 
       
  1953 			return EProcessed;	
       
  1954 	 		}
       
  1955 	 	else
       
  1956 	 		{
       
  1957 	 		// The session that is trying to perform this operation has not started the transaction
       
  1958 	 		return DeferWithTimeOutError(aRequest);
       
  1959 	 		}
       
  1960   		}
       
  1961   	else
       
  1962   		{
       
  1963   		// If the request can not be procesed after the timeout period, it should 
       
  1964   		// complete with KErrInUse as the contact is locked
       
  1965   		aRequest->SetTimeOutError(KErrInUse);  		
       
  1966   		}
       
  1967 
       
  1968 	return DeferRequest(aRequest);	
       
  1969 	}
       
  1970 
       
  1971 
       
  1972 /**
       
  1973  Commit (write & unlock) a contact item that has been locked to the database
       
  1974  The contact item is unlocked.
       
  1975  
       
  1976  @param aRequest The request that contain the contact item that is to be written to the database
       
  1977  @return TAccept EProcessed if finished processing request
       
  1978  		 	     EDeferred if the request was not processed 
       
  1979 */ 
       
  1980 TAccept CStateTransaction::AcceptRequestL(CReqCommitCnt* aRequest)
       
  1981 	{
       
  1982 	if (iStateMachine.LowDisk())
       
  1983 		{
       
  1984 		aRequest->Complete(KErrDiskFull);
       
  1985 		return EProcessed;
       
  1986 		}
       
  1987 
       
  1988  	if (iSessionId == aRequest->SessionId())
       
  1989  		{
       
  1990  		User::LeaveIfError(iStateMachine.TransactionLockL().UnLockL(aRequest->SessionId(), aRequest->Item().Id()));
       
  1991  		TRAPD(updateErr, iPersistenceLayer.PersistenceBroker().UpdateL(aRequest->Item(), aRequest->SessionId()));
       
  1992  		if (updateErr == KSqlErrGeneral)
       
  1993  			{
       
  1994  			// Can't update contact, probably due to idle sorter activity
       
  1995  			return DeferWithTimeOutError(aRequest);
       
  1996  			}
       
  1997  		else
       
  1998  			{
       
  1999  			User::LeaveIfError(updateErr);
       
  2000  			}
       
  2001  		
       
  2002 		aRequest->Complete();
       
  2003 		iTimeOut->Reset(); 
       
  2004 		
       
  2005 		return EProcessed;	
       
  2006 		}
       
  2007  	else
       
  2008  		{
       
  2009  		// The session that is trying to perform this operation has not started the transaction
       
  2010  		return DeferWithTimeOutError(aRequest);
       
  2011  		}
       
  2012 	}
       
  2013 
       
  2014 /**
       
  2015  Open (read and lock) the contact item, returning the opened contact item.
       
  2016  The contact item is also locked
       
  2017  
       
  2018  @param aRequest The request that will contain the contact item that is to be opened (read and locked) 
       
  2019  				 in the database
       
  2020  @return TAccept EProcessed if finished processing request
       
  2021  		 	     EDeferred if the request was not processed 
       
  2022 */ 
       
  2023 TAccept CStateTransaction::AcceptRequestL(CReqOpenCnt* aRequest)
       
  2024 	{
       
  2025  	if (iSessionId == aRequest->SessionId())
       
  2026  		{
       
  2027  		// As a valid operation has been performed by the session, it is still 
       
  2028  		// alive so the timeout for the transaction state must be reset.
       
  2029 		iTimeOut->Reset(); 
       
  2030 		return CState::AcceptRequestL(aRequest);
       
  2031  		}
       
  2032   	else
       
  2033  		{
       
  2034  		// The session that is trying to perform this operation has not started the transaction
       
  2035  		return DeferWithTimeOutError(aRequest);	
       
  2036  		}
       
  2037 	}
       
  2038 
       
  2039 /**
       
  2040  Close (unlock) the locked contact without commiting the contact item to the database 
       
  2041  only if the session also started the transaction
       
  2042  
       
  2043  @param  aRequest The request that contain the contact item that is to be closed (unlocked but not updated)
       
  2044  @return One of the values returned by DeferWithTimeOutError, or CState::AcceptRequest(CReqCloseCnt*)
       
  2045  @see CState::DeferWithTimeOutError
       
  2046  @see CState::AcceptRequest(CReqCloseCnt*)
       
  2047 */ 
       
  2048 TAccept CStateTransaction::AcceptRequestL(CReqCloseCnt* aRequest)
       
  2049 	{
       
  2050  	if (iSessionId == aRequest->SessionId())
       
  2051  		{
       
  2052 		iTimeOut->Reset(); 
       
  2053 		return CState::AcceptRequestL(aRequest);
       
  2054 		}
       
  2055  	else
       
  2056  		{
       
  2057  		// The session that is trying to perform this operation has not started the transaction
       
  2058  		return DeferWithTimeOutError(aRequest);
       
  2059  		}
       
  2060 	}
       
  2061 
       
  2062 /**
       
  2063  Hanlde a database event while in the transaction state.
       
  2064  The CStateTransaction holds all events, and does not propagate them to 
       
  2065  the DbManager, until an explicit commit request is called and the database
       
  2066  is written to. All events are not valid until a commit is called as a 
       
  2067  transaction rollback would invalidate the events.
       
  2068  
       
  2069  @param aEvent The database event that is being propogated.
       
  2070 */
       
  2071 void CStateTransaction::HandleDatabaseEventL(TContactDbObserverEvent aEvent)
       
  2072 	{
       
  2073 	
       
  2074 	DEBUG_PRINT1(__VERBOSE_DEBUG__,_L("[CNTMODEL] Database Event in Transaction"));
       
  2075 
       
  2076 	// Do not add a rollback event to the queue. This event will be propagated as 
       
  2077 	// part of the transaction rollback.
       
  2078 	if (aEvent.iType == EContactDbObserverEventRollback)
       
  2079 		{
       
  2080 		return;
       
  2081 		}
       
  2082 	if (iEventQ.Count() <= KMaxNumberOfEventsInEventQueue)
       
  2083 		{
       
  2084 		
       
  2085 		DEBUG_PRINT1(__VERBOSE_DEBUG__,_L("[CNTMODEL] Database Event Added To Q in Transaction"));
       
  2086 	
       
  2087 		iEventQ.AppendL(aEvent);	
       
  2088 		} 
       
  2089 	// else - do nothing as a EContactDbObserverEventUnknownChanges will be propagated
       
  2090 	// to all observers	
       
  2091 	}
       
  2092 
       
  2093 
       
  2094 /** 
       
  2095  Propagate events back to the CCntDbManager after a commit transaction request
       
  2096 */
       
  2097 void CStateTransaction::PropagateDatabaseEventsL()
       
  2098 	{
       
  2099 	if (iEventQ.Count() >= KMaxNumberOfEventsInEventQueue)
       
  2100 		{
       
  2101 		
       
  2102 		DEBUG_PRINT1(__VERBOSE_DEBUG__,_L("[CNTMODEL] Max Database Events reached in Transaction - Unknown changes event"));
       
  2103 
       
  2104 		// Tell the observer to re-sync with the database as there has been too many
       
  2105 		// operations for which it needs to recieve notification
       
  2106 		TContactDbObserverEvent unknownChangeEvent;
       
  2107 		unknownChangeEvent.iType 		 = EContactDbObserverEventUnknownChanges;
       
  2108 		unknownChangeEvent.iContactId 	 = KNullContactId;
       
  2109 		unknownChangeEvent.iConnectionId = KCntNullConnectionId;
       
  2110 		
       
  2111 		iStateMachine.DbManager().HandleDatabaseEventL(unknownChangeEvent);
       
  2112 		}
       
  2113 	else
       
  2114 		{
       
  2115 		
       
  2116 		DEBUG_PRINT1(__VERBOSE_DEBUG__,_L("[CNTMODEL] Database Events being propagated in Transaction"));
       
  2117 	
       
  2118 		TInt max = iEventQ.Count();
       
  2119 		TInt ii  = 0;
       
  2120 		// Propagate the events from the beginning of the EventQ
       
  2121 		while(ii != max) 
       
  2122 			{
       
  2123 			iStateMachine.DbManager().HandleDatabaseEventL(iEventQ[ii]);	
       
  2124 			++ii;	
       
  2125 			}
       
  2126 		}
       
  2127 	iEventQ.Reset(); // Empty the queue	
       
  2128 	}
       
  2129 
       
  2130 
       
  2131 
       
  2132 // CTransactionTimer Implementation //
       
  2133 
       
  2134 /** 
       
  2135  CTransactionTimer Class NewL factory constructor
       
  2136  Create a transaction timer
       
  2137 
       
  2138  The transaction timer is used to allow a transaction to timeout should
       
  2139  the session which put the state machine into a transaction state die
       
  2140  unexpectedly. It is derived from CTimer and times out after sixty seconds.
       
  2141 */ 
       
  2142 CTransactionTimer* CTransactionTimer::NewL(CStateTransaction& aTransState)
       
  2143 	{
       
  2144 	CTransactionTimer* self = new (ELeave) CTransactionTimer(aTransState);
       
  2145 	CleanupStack::PushL(self);
       
  2146 	self->ConstructL();
       
  2147 	CleanupStack::Pop(self);
       
  2148 	return self;
       
  2149 	}
       
  2150 	
       
  2151 /** 
       
  2152  CTransactionTimer Class destructor
       
  2153 */	
       
  2154 CTransactionTimer::~CTransactionTimer()
       
  2155 	{
       
  2156 	CTimer::Cancel();
       
  2157 	}
       
  2158 	
       
  2159 /**
       
  2160  CTransactionTimer constructor
       
  2161 */	
       
  2162 CTransactionTimer::CTransactionTimer(CStateTransaction& aTransState) 
       
  2163 				 : CTimer(CActive::EPriorityIdle), iTransState(aTransState)
       
  2164 	{
       
  2165 	}
       
  2166 
       
  2167 void CTransactionTimer::ConstructL()
       
  2168 	{
       
  2169 	CTimer::ConstructL();
       
  2170 	CActiveScheduler::Add(this);
       
  2171 	}
       
  2172 
       
  2173 
       
  2174 /** 
       
  2175  The Transaction was neither commited nor rolled back 
       
  2176  The client may have died - clean up the state machine by rolling back
       
  2177 */ 
       
  2178 void CTransactionTimer::RunL()
       
  2179 	{
       
  2180 	iTransState.CancelTransactionL();
       
  2181 	CTimer::Cancel();
       
  2182 	}
       
  2183 
       
  2184 /**
       
  2185  Start the timer. This is done when a the state machine moves 
       
  2186  into the transaction state.
       
  2187 */
       
  2188 void CTransactionTimer::Start()
       
  2189 	{ // wait for 60 seconds
       
  2190 	CTimer::Cancel();
       
  2191 	CTimer::After(KSixtySeconds);
       
  2192 	}
       
  2193 
       
  2194 /**
       
  2195  Stop the timer. This is done when a the state machine moves 
       
  2196  out of the transaction state.
       
  2197 */
       
  2198 void CTransactionTimer::Stop()
       
  2199 	{
       
  2200 	CTimer::Cancel();
       
  2201 	}
       
  2202 
       
  2203 /**
       
  2204  When a valid operation is performed within the transaction state
       
  2205  the timer is reset to sixty seconds, if another transaction operation is not
       
  2206  performed within sixty seconds, the transaction should timeout.
       
  2207 */
       
  2208 void CTransactionTimer::Reset()
       
  2209 	{
       
  2210 	CTimer::Cancel();
       
  2211 	CTimer::After(KSixtySeconds);
       
  2212 	}
       
  2213 	
       
  2214 // CTransactionLock Class Implementation //
       
  2215 /** 
       
  2216  CTransactionLock Class NewL factory constructor
       
  2217  The CTransactionLock class locks contacts allowing only the locking session to
       
  2218  modify the contact item in the database.
       
  2219 */
       
  2220 CTransactionLock* CTransactionLock::NewL(CCntStateMachine& aStateMachine)
       
  2221 	{
       
  2222 	CTransactionLock* self = new (ELeave) CTransactionLock(aStateMachine);
       
  2223 	return self;
       
  2224 	}
       
  2225 	
       
  2226 // TLockData constructor	
       
  2227 CTransactionLock::TLockData::TLockData(TContactItemId aCntId, const TUint aSessionId):iCntItemId(aCntId), iSessionId(aSessionId)
       
  2228 	{}
       
  2229 	
       
  2230 // --------- Locking Methods -----------
       
  2231 /** 
       
  2232  Locks a contact item by adding its ID to an array of locked contact items IDs.
       
  2233  After a session locks the contact item, no other session can modify the locked contact
       
  2234  item. 
       
  2235  Adds the lock to the clean up stack, indicated by the 'X' in the method name
       
  2236  
       
  2237  @param aCntId The ID of contact item to be locked
       
  2238  @param aSessionId The Session which is locking the contact Item
       
  2239  aContact The contact item to add to the database.
       
  2240  @return KErrNone if locking was successful.
       
  2241 		KErrInUse if the contact item was locked by another session 
       
  2242 */
       
  2243 
       
  2244 TInt CTransactionLock::LockLX(const TUint aSessionId, const TContactItemId aCntId)
       
  2245 	{
       
  2246 	
       
  2247 	DEBUG_PRINT2(__VERBOSE_DEBUG__,_L("[CNTMODEL] *   Lock item %d"), aCntId);
       
  2248 	
       
  2249 	if (IsLocked(aCntId))
       
  2250 		{
       
  2251 		return KErrInUse; // A session can only lock a cnt item once.	
       
  2252 		}
       
  2253 		
       
  2254 	TLockData lockData(aCntId, aSessionId);
       
  2255 	
       
  2256 	iLockedIds.InsertInSignedKeyOrderL(lockData);
       
  2257 	
       
  2258 	CleanupStack::PushL(TCleanupItem(CTransactionLock::CleanupUnlockRecord, this));
       
  2259 		
       
  2260 	return KErrNone;	
       
  2261 	}
       
  2262 
       
  2263 	
       
  2264 /**
       
  2265  Unlocks the last locked contact item after a leave
       
  2266  
       
  2267  @param aTransLock The CTransactionLock object from which the last locked contact item
       
  2268  				  ID must be removed (unlocked).
       
  2269 */
       
  2270 void CTransactionLock::CleanupUnlockRecord(TAny* aTransLock)
       
  2271 	{
       
  2272 	TRAP_IGNORE(static_cast<CTransactionLock*>(aTransLock)->UnlockLastLockedContactL() );
       
  2273 	}	
       
  2274 	
       
  2275 /** 
       
  2276  UnLocks a contact item by removing its ID to an array of locked contact items IDs.
       
  2277  
       
  2278  @param aCntId The ID of contact item to be unlocked
       
  2279  @param aSessionId The Session which is unlocking the contact Item
       
  2280  @return KErrNone if locking was successful.
       
  2281  		 KErrAccessDenied if the contact item was not successfuly locked  
       
  2282 */	
       
  2283 TInt CTransactionLock::UnLockL(const TUint aSessionId, const TContactItemId aCntId)
       
  2284 	{
       
  2285 	
       
  2286 	DEBUG_PRINT2(__VERBOSE_DEBUG__,_L("[CNTMODEL] * UnLock item %d"), aCntId);
       
  2287 	
       
  2288 	TLockData lockData(aCntId, aSessionId);
       
  2289 	TInt index = iLockedIds.FindInSignedKeyOrder(lockData);
       
  2290 	if (index < 0)
       
  2291 		return KErrAccessDenied;
       
  2292 	
       
  2293 	if (index > iLockedIds.Count())
       
  2294 		{
       
  2295 		return KErrAccessDenied;	
       
  2296 		}
       
  2297 	
       
  2298 	if (iLockedIds[index].iSessionId == aSessionId)
       
  2299 		{
       
  2300 		iLockedIds.Remove(index);
       
  2301 		ProcessLockedContactsL(); // Process any requests in the Store
       
  2302 		}
       
  2303 	return KErrNone;	
       
  2304 	}
       
  2305 
       
  2306 /**
       
  2307  Process any requests in the Store - Another session
       
  2308  may have been trying to perform an operation on a  
       
  2309  locked contact item. This method is called after a contact item
       
  2310  has been unlocked to allow an operation on the same contact item
       
  2311  to be performed by another session.
       
  2312 */
       
  2313 void CTransactionLock::ProcessLockedContactsL()
       
  2314 	{
       
  2315 	if(iStateMachine.ReqStoreL().IsEmpty() == EFalse)
       
  2316 		{
       
  2317 		iStateMachine.ReqStoreL().ActivateRequestsL();	
       
  2318 		}		
       
  2319 	}
       
  2320 
       
  2321 /**
       
  2322  Unlocks all the locked contacts for a given sessionid, the request 
       
  2323  that calls this method, CReqInternalSessionUnlock, originates in the session destructor
       
  2324  
       
  2325  @param aSessionId The session that is being closed.
       
  2326 */ 
       
  2327 void CTransactionLock::UnLockAllL(const TUint aSessionId)
       
  2328 	{
       
  2329 	TInt ii	= iLockedIds.Count();
       
  2330 	while(ii) 
       
  2331 		{
       
  2332 		--ii;		
       
  2333 		if (iLockedIds[ii].iSessionId == aSessionId)
       
  2334 			{
       
  2335 			iLockedIds.Remove(ii);
       
  2336 			}
       
  2337 		}
       
  2338 	ProcessLockedContactsL(); // Process any requests in the Store
       
  2339 	}
       
  2340 	
       
  2341 /** 
       
  2342  Unlock the last locked contact after an leave has occured	
       
  2343  
       
  2344  @param aSessionId The ID of the session that performed the operation in which 
       
  2345  				   the leave occured.
       
  2346 */
       
  2347 void CTransactionLock::UnlockLastLockedContactL(TUint aSessionId)	
       
  2348 	{
       
  2349 	if (aSessionId == nsState::KNoSessionId)
       
  2350 		{
       
  2351 		// Remove the last Locked Contact regardless of session
       
  2352 		iLockedIds.Remove(iLockedIds.Count() - 1);
       
  2353 		ProcessLockedContactsL(); // Process any requests in the Store
       
  2354 		return;
       
  2355 		}
       
  2356 	
       
  2357 	TInt ii	= iLockedIds.Count();
       
  2358 	while(ii) 
       
  2359 		{
       
  2360 		--ii;		
       
  2361 		if (iLockedIds[ii].iSessionId == aSessionId)
       
  2362 			{
       
  2363 			iLockedIds.Remove(ii);
       
  2364 			ProcessLockedContactsL(); // Process any requests in the Store
       
  2365 			return; // Finished
       
  2366 			}
       
  2367 		}
       
  2368 	}
       
  2369 	
       
  2370 /** 
       
  2371 Checks if a contact item is locked by another session.
       
  2372 
       
  2373 @param aCntId The ID of contact item to be checked
       
  2374 @param aSessionId The Session which is checking for a lock
       
  2375 
       
  2376 @return True if the contact has been locked.
       
  2377 		False if the contact has not been locked by another session.
       
  2378 */
       
  2379 TBool CTransactionLock::IsLocked(const TUint aSessionId, const TContactItemId aCntId) const
       
  2380 	{
       
  2381 	TInt ii	= iLockedIds.Count();
       
  2382 	
       
  2383 	while(ii) 
       
  2384 		{
       
  2385 		--ii;		
       
  2386 		if (iLockedIds[ii].iCntItemId == aCntId)
       
  2387 			{
       
  2388 			if (iLockedIds[ii].iSessionId != aSessionId)
       
  2389 				{
       
  2390 				return ETrue;	// locked by another session	
       
  2391 				}
       
  2392 			return EFalse; // has not been locked by another session	
       
  2393 			}
       
  2394 		}
       
  2395 	return EFalse; // has not been locked by any session
       
  2396 	}
       
  2397 
       
  2398 /**
       
  2399  Checks if a contact item is locked by this or another session (any session).
       
  2400  Original behaviour of the contacts model was not to allow a session to lock a contact twice.
       
  2401 
       
  2402  @param aCntId The ID of contact item to be checked
       
  2403  @return True if the contact has been locked.
       
  2404 		False if the contact has not been locked.
       
  2405 */
       
  2406 TBool CTransactionLock::IsLocked(const TContactItemId aCntId)const
       
  2407 	{
       
  2408 	TInt ii = iLockedIds.Count();
       
  2409 	
       
  2410 	while(ii) 
       
  2411 		{
       
  2412 		--ii;		
       
  2413 		if (iLockedIds[ii].iCntItemId == aCntId)
       
  2414 			{
       
  2415 			return ETrue;	// locked 
       
  2416 			}
       
  2417 		}
       
  2418 	return EFalse;
       
  2419 	}
       
  2420 
       
  2421 /**
       
  2422  Determines if any contacts items are locked by this or another session (any
       
  2423  session).
       
  2424  @return True if there is a locked contact.
       
  2425 		False if there is no locked contact.
       
  2426 */
       
  2427 TBool CTransactionLock::AnyLocked() const
       
  2428 	{
       
  2429 	return iLockedIds.Count() != 0;
       
  2430 	}
       
  2431 
       
  2432 /** CStateBackupRestore
       
  2433 	While in this state neither read nor write operations are allowed.  The
       
  2434 	CStateClosed parent class AcceptRequestL() handles all read and write
       
  2435 	requests.
       
  2436 	
       
  2437 	Methods completes the request with KErrLocked via a call to TimeOutErrorCode() 
       
  2438 	from the parent CStateClosed class.
       
  2439 */
       
  2440 CStateBackupRestore* CStateBackupRestore::NewL(CCntStateMachine& aStateMachine, CPersistenceLayer& aPersistenceLayer)
       
  2441 	{
       
  2442 	CStateBackupRestore* stateBUR = new (ELeave) CStateBackupRestore(aStateMachine, aPersistenceLayer);
       
  2443 	return stateBUR;
       
  2444 	}
       
  2445 	
       
  2446 	
       
  2447 CStateBackupRestore::~CStateBackupRestore()
       
  2448 	{
       
  2449 	}
       
  2450 
       
  2451 
       
  2452 CStateBackupRestore::CStateBackupRestore(CCntStateMachine& aStateMachine, CPersistenceLayer& aPersistenceLayer)
       
  2453 :CStateClosed(aStateMachine, aPersistenceLayer)
       
  2454 	{
       
  2455 	}
       
  2456 
       
  2457 
       
  2458 // Default AsyncOpen requests behaviour to the base implementation
       
  2459 TAccept CStateBackupRestore::AcceptRequestL(CReqAsyncOpen* aRequest)
       
  2460 	{
       
  2461 	return CState::AcceptRequestL(aRequest);
       
  2462 	}
       
  2463 
       
  2464 
       
  2465 TAccept CStateBackupRestore::AcceptRequestL(CReqBackupRestoreBegin* aRequest)
       
  2466   	{
       
  2467   	// A backup/restore is already in progress so don't defer this request -
       
  2468   	// simply complete it.
       
  2469 	aRequest->Complete();
       
  2470   	return EProcessed;
       
  2471   	}	
       
  2472 
       
  2473 
       
  2474 TAccept CStateBackupRestore::AcceptRequestL(CReqBackupRestoreEnd* aRequest)
       
  2475 	{
       
  2476 	// Once Backup/Restore completes we can re-open the database.  Re-accept
       
  2477 	// request in CStateOpening.	
       
  2478 	iStateMachine.SetCurrentStateL(iStateMachine.StateOpening());  
       
  2479 	return iStateMachine.CurrentState().AcceptRequestL(aRequest);
       
  2480  	}
       
  2481 
       
  2482 
       
  2483 /**
       
  2484  If there was asynchronous activity then it has now finished so safe to close
       
  2485  database.
       
  2486  
       
  2487  @param aRequest Notification of end of async activity request object
       
  2488  @return TAccept EProcessed - finished processing request
       
  2489 */  
       
  2490 TAccept CStateBackupRestore::AcceptRequestL(CReqNoAsyncActivity* aRequest)
       
  2491   	{
       
  2492   	if (iStateMachine.AsyncActivity())
       
  2493   		{
       
  2494   		iStateMachine.SetAsyncActivity(EFalse);
       
  2495 		// Close the file to allow the backup/restore to take place.	
       
  2496 		iPersistenceLayer.ContactsFileL().Close();
       
  2497 		}
       
  2498   	aRequest->Complete();
       
  2499   	return EProcessed;
       
  2500   	}
       
  2501 
       
  2502 /** 
       
  2503  Returns the default error code - KErrLocked - for the backup restore state 
       
  2504 */
       
  2505 TInt CStateBackupRestore::TimeOutErrorCode()  	
       
  2506 	{
       
  2507 	return KErrLocked;
       
  2508 	}
       
  2509 
       
  2510 
       
  2511 // ======================================		
       
  2512 // CCntStateMachine Class implementation	
       
  2513 // The main purpose of the CState object 
       
  2514 // is to define the state transition table
       
  2515 	
       
  2516 CCntStateMachine::~CCntStateMachine()
       
  2517 	{
       
  2518 	iStateArray.ResetAndDestroy();
       
  2519 	delete iReqStore;
       
  2520 	delete iTransLock;
       
  2521 	}
       
  2522 	
       
  2523 CCntStateMachine* CCntStateMachine::NewL(CPersistenceLayer& aPersistenceLayer, CCntDbManager& aDbManager)
       
  2524 	{
       
  2525 	CCntStateMachine* stateMachine = new (ELeave) CCntStateMachine(aDbManager);
       
  2526 	CleanupStack::PushL(stateMachine);
       
  2527 	stateMachine->ConstructL(aPersistenceLayer);
       
  2528 	CleanupStack::Pop(stateMachine);
       
  2529 	return stateMachine;
       
  2530 	}
       
  2531 	
       
  2532 /** 
       
  2533  Create and add all states to the state machine array	
       
  2534  
       
  2535  @param aPersistenceLayer. The persistence layer that wraps up access to the database.
       
  2536 */
       
  2537 void CCntStateMachine::ConstructL(CPersistenceLayer& aPersistenceLayer)
       
  2538 	{
       
  2539 	iState = CStateClosed::NewL(*this, aPersistenceLayer);
       
  2540 	CleanupStack::PushL(iState);
       
  2541 	// The order in which states are appended must be in sync with
       
  2542 	// the declaration order of the state enum.
       
  2543 	iStateArray.AppendL(iState); 
       
  2544 	CleanupStack::Pop(iState);
       
  2545 	
       
  2546 	iStateArray.AppendL(CStateTablesClosed::NewL(*this, aPersistenceLayer)); 
       
  2547 	iStateArray.AppendL(CStateWritable	  ::NewL(*this, aPersistenceLayer));
       
  2548 	iStateArray.AppendL(CStateOpening 	  ::NewL(*this, aPersistenceLayer));
       
  2549 	iStateArray.AppendL(CStateTransaction ::NewL(*this, aPersistenceLayer));
       
  2550 	iStateArray.AppendL(CStateBackupRestore::NewL(*this, aPersistenceLayer));
       
  2551 	}
       
  2552 
       
  2553 /**
       
  2554  Get the current active state		
       
  2555  
       
  2556  @return The current active state
       
  2557 */ 
       
  2558 CState& CCntStateMachine::CurrentState()
       
  2559 	{
       
  2560 	return *iState;
       
  2561 	}
       
  2562 	
       
  2563 /** 
       
  2564  Get the transaction lock	
       
  2565  
       
  2566  @return The Transaction Lock object
       
  2567 */
       
  2568 CTransactionLock& CCntStateMachine::TransactionLockL()
       
  2569 	{
       
  2570 	if (!iTransLock)
       
  2571 		{
       
  2572 		iTransLock = CTransactionLock::NewL(*this);			
       
  2573 		}
       
  2574 
       
  2575 	return *iTransLock;
       
  2576 	}
       
  2577 
       
  2578 /** 
       
  2579  Get the Database Manager
       
  2580  
       
  2581  @return the Contact Database Manager object
       
  2582 */
       
  2583 CCntDbManager& CCntStateMachine::DbManager()
       
  2584 	{
       
  2585 	return iDbManager;
       
  2586 	}
       
  2587 
       
  2588 /**
       
  2589  StateMachine constructor
       
  2590  
       
  2591  @param aDbManager The Database Manager.
       
  2592 */
       
  2593 CCntStateMachine::CCntStateMachine(CCntDbManager& aDbManager)
       
  2594 	:
       
  2595   	iDbManager(aDbManager),
       
  2596   	iLowDisk(EFalse),
       
  2597   	iAsyncActivity(EFalse)
       
  2598 	{
       
  2599 	// Nothing to do.
       
  2600 	}
       
  2601 
       
  2602 /**
       
  2603  Used for debugging the transition between state. 
       
  2604  Define __STATE_MACHINE_DEBUG__ in the project definition file (mmp).
       
  2605  
       
  2606  @param aState The state which is becoming active
       
  2607  @return A descriptor containing the active state name.
       
  2608 */ 
       
  2609 #ifdef __STATE_MACHINE_DEBUG__
       
  2610 const TDesC& CCntStateMachine::StateName(CState& aState)
       
  2611 	{
       
  2612 	_LIT(KStateClosedName,   	"EStateClosed");
       
  2613 	_LIT(KStateTablesClosed, 	"EStateTablesClosed");
       
  2614 	_LIT(KStateWritableName, 	"EStateWritable");
       
  2615 	_LIT(KStateOpeningName,  	"EStateOpening");
       
  2616 	_LIT(KStateTransactionName, "EStateTransaction");
       
  2617 	_LIT(KStateBackupRestoreName, "EStateBackupRestore");
       
  2618 	_LIT(KStateUnknownName,  	"Unknown State");
       
  2619 
       
  2620 	if (&aState == &StateClosed())
       
  2621 		{
       
  2622 		return KStateClosedName();
       
  2623 		}
       
  2624 		
       
  2625 	if (&aState == &StateTablesClosed())
       
  2626 		{
       
  2627 		return KStateTablesClosed();	
       
  2628 		}
       
  2629 		
       
  2630 	if (&aState == &StateWritable())
       
  2631 		{
       
  2632 		return KStateWritableName();
       
  2633 		}
       
  2634 
       
  2635 	if (&aState == &StateOpening())
       
  2636 		{
       
  2637 		return KStateOpeningName();
       
  2638 		}
       
  2639 
       
  2640 	if (&aState == &StateTransaction())
       
  2641 		{
       
  2642 		return KStateTransactionName();
       
  2643 		}
       
  2644 
       
  2645 	if (&aState == &StateBackupRestore())
       
  2646 		{
       
  2647 		return KStateBackupRestoreName();
       
  2648 		}
       
  2649 
       
  2650 	return KStateUnknownName();
       
  2651 	}
       
  2652 #endif
       
  2653 
       
  2654 /** 
       
  2655  Set the active state in the state machine	
       
  2656  
       
  2657  @param aState The state which is becoming active
       
  2658 */
       
  2659 void CCntStateMachine::SetCurrentStateL(CState& aState)
       
  2660 	{
       
  2661 	
       
  2662 #ifdef __STATE_MACHINE_DEBUG__
       
  2663 	RDebug::Print(_L("[CNTMODEL] STA: %S --> %S\r\n"),	&CCntStateMachine::StateName(*iState), &CCntStateMachine::StateName(aState));
       
  2664 #endif
       
  2665 
       
  2666 	iState = &aState;
       
  2667 	// Process any requests in the Store on each state change
       
  2668 	// The state may have changed to one where queued requests 
       
  2669 	// can now be processed.
       
  2670 	if(ReqStoreL().IsEmpty() == EFalse)
       
  2671 		{
       
  2672 		iReqStore->ActivateRequestsL();	
       
  2673 		}
       
  2674 	}
       
  2675 
       
  2676 /** 
       
  2677  This is the interface to the state machine (used by the session class). 
       
  2678  The state machine is asked to process a request by the session. It does this
       
  2679  by passing the  current active state to the request object (VisitStateL). 
       
  2680  The request then calls AcceptRequest on the current active state object. The
       
  2681  state and the request are completely decoupled.
       
  2682  
       
  2683  Caller of ProcessRequestL should assume transfer request ownership unless 
       
  2684  ProcessRequestL leaves.  In the event ProcessRequestL leaves, the original owner
       
  2685  of the request is responsible for the cleanup of the request.  
       
  2686  
       
  2687  Note that if the request is to be transferred to another object, ensure once the 
       
  2688  transfer has taken place, no leave should occur, as this will trigger both the new 
       
  2689  owner and the original owner to cleanup the request.
       
  2690  
       
  2691  @param aRequest A request that is being processed.
       
  2692 */ 
       
  2693 void CCntStateMachine::ProcessRequestL(CCntRequest* aRequest)
       
  2694     {
       
  2695 	// Obtained ownership of the request object.  It is the responsibility
       
  2696 	// of this function to ensure the request is properly disposed after being 
       
  2697 	// processed unless leave occurs.
       
  2698     TAccept result = aRequest->VisitStateL(*iState);
       
  2699 
       
  2700     switch(result)
       
  2701         {
       
  2702         case EDeferred:
       
  2703             // The visited state cannot process the request at the moment.  Activate the 
       
  2704             // request's timer and transfer ownership of the request to the request store.  
       
  2705             // Hopefully, a state change happens before timeout occurs, when the new state 
       
  2706             // will attempt to process all deferred request.  If timeout occurs before 
       
  2707             // a change of state, the request will be completed with its' timeout error code.  
       
  2708             // This timeout error code can be set thru the CCntRequest API SetTimeOutError.
       
  2709             aRequest->ActivateTimeOutL(ReqStoreL());
       
  2710             ReqStoreL().AppendL(aRequest);  // ownership transferred
       
  2711             break;
       
  2712 
       
  2713         case EProcessed:
       
  2714 			// The request has been processed by the visited state - nothing more
       
  2715 			// to do, except to destroy the request now
       
  2716             delete aRequest;
       
  2717             break;
       
  2718 
       
  2719         case EOwnershipPassed:
       
  2720             // the request has been hi-jacked by the visited state, the new owner will
       
  2721             // assume responsibility of cleanup, no need to do anything here.
       
  2722         default:
       
  2723             break;
       
  2724         }
       
  2725     }
       
  2726 
       
  2727 
       
  2728 /**
       
  2729  Allow the active state to process the event. The only state that actually
       
  2730  processes events is the Transaction State. All other states (via the base state CState) 
       
  2731  simply propagate the event back to the CCntDbManager, from where it is propogated to
       
  2732  each CCntSession contained within the CCntDbManager.
       
  2733  
       
  2734  @param aEvent The database event that is being propagated.
       
  2735 */ 
       
  2736 void CCntStateMachine::HandleDatabaseEventL(TContactDbObserverEvent aEvent)
       
  2737 	{
       
  2738 	#if defined(__PROFILE_DEBUG__)
       
  2739 		RDebug::Print(_L("[CNTMODEL] MTD: CCntStateMachine::HandleDatabaseEventL"));
       
  2740 	#endif 
       
  2741 	
       
  2742 	iState->HandleDatabaseEventL(aEvent);
       
  2743 	}
       
  2744 
       
  2745 /** 
       
  2746  Create the state machines request store. When a request cannot be processed in
       
  2747  the active state, it is deferred until the active state changes or a contact item is unlocked.
       
  2748  That is the request in this ReqStore is processed by the state machine - again - after either 
       
  2749  a state change or unlock operation. 
       
  2750  The store takes a reference to the CCntStateMachine in order to ProcessRequestL
       
  2751  
       
  2752  @return The Request Store  	
       
  2753 */ 
       
  2754 CRequestStore& CCntStateMachine::ReqStoreL()
       
  2755 	{
       
  2756 	if (!iReqStore)
       
  2757 		{
       
  2758 		iReqStore = CRequestStore::NewL(*this);	
       
  2759 		}
       
  2760 	return *iReqStore;	
       
  2761 	}