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